├── .babelrc ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README_zhTW.md ├── TODO.md ├── cardbattle.gif ├── contracts ├── LOLYAT.sol ├── LowBit.sol └── Migrations.sol ├── dapp ├── .babelrc ├── .gitignore ├── CARD.md ├── README.md ├── config │ ├── env.js │ ├── jest │ │ ├── cssTransform.js │ │ └── fileTransform.js │ ├── paths.js │ ├── polyfills.js │ ├── webpack.config.dev.js │ ├── webpack.config.prod.js │ └── webpackDevServer.config.js ├── package.json ├── public │ ├── audio │ │ └── arcades.mp3 │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── scripts │ ├── build.js │ ├── start.js │ └── test.js └── src │ ├── .DS_Store │ ├── actions │ ├── LOLYATActions.js │ ├── counterActions.js │ ├── fetchingActions.js │ ├── healthActions.js │ ├── metaMaskActions.js │ ├── simpleTokenActions.js │ └── warningActions.js │ ├── apis │ ├── api.http │ └── api.js │ ├── components │ ├── App.css │ ├── App.js │ ├── App.scss │ ├── App.test.js │ ├── Arena │ │ ├── Arena.css │ │ ├── Arena.scss │ │ └── index.js │ ├── BattleCard │ │ ├── index.js │ │ ├── style.css │ │ └── style.scss │ ├── Card │ │ ├── Card.css │ │ ├── Card.scss │ │ └── index.js │ ├── Contract.css │ ├── Contract.js │ ├── Contract.scss │ ├── Counter.js │ ├── Health.js │ ├── IndexUi.js │ ├── Loading │ │ ├── Loading.css │ │ ├── Loading.scss │ │ └── index.js │ ├── LoadingCoin │ │ ├── index.js │ │ ├── style.css │ │ └── style.scss │ ├── MetaMask │ │ └── MetaMask.js │ ├── NiftyAlert │ │ └── index.js │ ├── SendTransaction.js │ ├── Top │ │ ├── Title.js │ │ └── Top.js │ ├── Tutorial │ │ ├── Tutorial.css │ │ ├── Tutorial.scss │ │ └── index.js │ ├── Warning │ │ └── Warning.js │ ├── _mixinKeyframes.scss │ ├── indexUi.css │ └── indexUi.scss │ ├── constants │ └── actionTypes.js │ ├── container │ └── AppContainer.js │ ├── images │ ├── .DS_Store │ ├── Elf0.png │ ├── Elf1.png │ ├── Elf2.png │ ├── Elf3.png │ ├── Tutorial.png │ ├── addcard.png │ ├── back.png │ ├── bgfooter.png │ ├── big.png │ ├── cardtitle.png │ ├── cloud.png │ ├── contarctresult.png │ ├── demo │ │ ├── 15.png │ │ ├── Pixel15.png │ │ └── bg15.png │ ├── draw.png │ ├── draw_message.png │ ├── footer.png │ ├── fruit1.png │ ├── fruit2.png │ ├── game.png │ ├── gameplaybtn.png │ ├── gameplaytitle.png │ ├── getcard.png │ ├── getherobtn.png │ ├── github-icon.png │ ├── github-icon2.png │ ├── goBack.png │ ├── history.png │ ├── inputETH.png │ ├── ladder.png │ ├── loading.png │ ├── loadingicoin.png │ ├── logo.png │ ├── lostelf.png │ ├── loveicon.png │ ├── paayer.png │ ├── playerbet.png │ ├── playgame.png │ ├── result.png │ ├── select.png │ ├── star1.png │ ├── star2.png │ ├── testCard1.png │ ├── title.png │ ├── title2.png │ ├── titlelogo.png │ ├── tree1.png │ ├── tree2.png │ ├── user │ │ ├── user1.png │ │ ├── user10.png │ │ ├── user11.png │ │ ├── user12.png │ │ ├── user13.png │ │ ├── user14.png │ │ ├── user2.png │ │ ├── user3.png │ │ ├── user4.png │ │ ├── user5.png │ │ ├── user6.png │ │ ├── user7.png │ │ ├── user8.png │ │ └── user9.png │ ├── userresult.png │ ├── winlogo.png │ └── youlost.png │ ├── index.css │ ├── index.js │ ├── lib │ ├── LOLYAT.js │ ├── LOLYAT.json │ ├── LOLYATService.js │ ├── LOLYATService.test.js │ ├── LowBit.js │ ├── LowBitService.js │ ├── SimpleToken.json │ ├── cryptoHerosGame.json │ ├── simpleToken.js │ ├── tokenService.js │ ├── tokenService.test.js │ └── web3Service.js │ ├── reducers │ ├── LOLYATOwnedReducer.js │ ├── LOLYATOwnedTokenURIReducer.js │ ├── LOLYATReducer.js │ ├── counterReducer.js │ ├── fetchingReducer.js │ ├── healthReducer.js │ ├── index.js │ ├── initialState.js │ ├── metaMaskReducer.js │ ├── simpleTokenReducer.js │ ├── systemReducer.js │ └── warningReducer.js │ ├── registerServiceWorker.js │ ├── routes.js │ ├── sagas │ ├── LOLYATOwnedSaga.js │ ├── LOLYATOwnedTokenURISaga.js │ ├── LOLYATSaga.js │ ├── healthSaga.js │ ├── index.js │ ├── simpleTokenSaga.js │ └── watcher.js │ └── store │ └── configureStore.js ├── gamehistory.gif ├── herocollection.gif ├── icon.png ├── migrations ├── 1_initial_migration.js └── 2_deploy_contract.js ├── package.json ├── test ├── cryptoHerosToken.test.js └── util.js └── truffle-config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | yarn.lock 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "9" 4 | - "8" 5 | - "7" 6 | - "6" -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Issues 4 | 5 | We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. 6 | 7 | ## Pull Requests 8 | 9 | * Fork the repo and create your branch from master. 10 | * If you've added code that should be tested, add tests. 11 | * Ensure the test suite passes. 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 Portal Network. https://www.portal.network 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

14 | 15 | > Non-fungible token game 16 | 17 | 18 | 19 | This project is a crypto card game that uses [OpenZepplin](https://github.com/OpenZeppelin/openzeppelin-solidity). 20 | 21 | The contract is compiled and deployed under the ERC-721 non-fungible token standard with truffle framework. After deploying onto the Ethereum blockchain, users will be able to play the game by interacting with the smart contract through DAPP’s front-end interface. 22 | 23 | Acquiring game card: 24 | Users can acquire game cards using ETH. Every game card will have a game point on it, which will be used to determine the winner later in the game. 25 | 26 | ## Roles 27 | 28 | #### Card Collection 29 | Players can aquire card using ether. Each card will have a random points on it, which will be used to deternmine the winner in the card game. 30 | 31 | ![Card Collection](herocollection.gif) 32 | 33 | #### Card Battle 34 | Once entered the game, players will need to choose a card to play for the round. Each round, the smart contract will randomly decide either card with larger or smaller point wins the round. At the same time, the smart contract will also randomly generate a number in order to compete with the player. Winner of the game will receive the price. 35 | 36 | ![Card Battle](cardbattle.gif) 37 | 38 | #### Dashboard 39 | You can view the card battle history about all the games you played. 40 | 41 | ![Game History](gamehistory.gif) 42 | 43 | ## ERC-721 Token 44 | 45 | ERC-721 non-fungible token: 46 | ERC-721 is a free, open standard that describes how to build non-fungible or unique tokens on the Ethereum blockchain. While most tokens are fungible (every token is the same as every other token), ERC-721 tokens are all unique (with unique ID). 47 | 48 | [Reference](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) 49 | 50 | 51 | ## Contracts 52 | You can find contract detail under `contracts/` directory: 53 | - [`CryptoHerosGame.sol`](./contracts/CryptoHerosGame.sol): 54 | The implementation of game execution and rule. 55 | 56 | - [`CryptoHerosToken.sol`](./contracts/CryptoHerosToken.sol): 57 | The implementation of game cards purchase and generation. 58 | 59 | ## Technical stack 60 | 61 | ### Frontend 62 | - React 63 | - Redux 64 | - Saga 65 | - Web3(MetaMask) 66 | 67 | ### UI 68 | - Sass 69 | - Material-UI 70 | 71 | ### Smart contract/Solidity 72 | - Truffle 73 | 74 | ### Test environment/Private chain 75 | - ganache 76 | 77 | ## Requirements 78 | 79 | * NodeJS 8.0+ recommended. 80 | * Windows, Linux or Mac OS X. 81 | 82 | ## How To Install Dependencies 83 | 84 | First install required dependencies: 85 | 86 | You'll need local ethereum node, I recommend `ganache-cli`. You can install it from npm. 87 | 88 | ``` 89 | npm install -g ganache-cli 90 | ``` 91 | 92 | Install truffle: 93 | 94 | ``` 95 | npm install -g truffle 96 | ``` 97 | 98 | Then install contract dependencies: 99 | 100 | ``` 101 | npm install 102 | ``` 103 | 104 | ## How To Test 105 | 106 | First make sure that local ethereum node is running. Execute: 107 | 108 | ``` 109 | ganache-cli --gasLimit 0xffffffffff -p 8545 110 | ``` 111 | 112 | Now you can compile and deploy contracts: 113 | 114 | ``` 115 | truffle compile && truffle migrate 116 | ``` 117 | 118 | Run contract tests: 119 | 120 | ``` 121 | truffle test 122 | ``` 123 | 124 | ## Playground 125 | 126 | We already deployed contracts to [Ropsten](https://ropsten.etherscan.io/) network. You can play with them RIGHT NOW. 127 | 128 | | Contract | Token address | Transaction hash 129 | |------------------|---------------|--------------------- 130 | | CryptoHerosGame | [0xb4FF27d8cD1C5b1e3D4BD8A8FFEBdA9BE9517a4b](https://ropsten.etherscan.io/address/0xb4ff27d8cd1c5b1e3d4bd8a8ffebda9be9517a4b) | [0x49bb8698e2951a0c7eb091038b500694cdf37c74ec51d6c98d91823dc9595b95](https://ropsten.etherscan.io/tx/0x49bb8698e2951a0c7eb091038b500694cdf37c74ec51d6c98d91823dc9595b95) 131 | | CryptoHerosToken | [0xa82Bc392bF65d03A796E1666d27594fB31De4B93](https://ropsten.etherscan.io/address/0xa82bc392bf65d03a796e1666d27594fb31de4b93) | [0xf41868e6b59020965831aac218e1a521b283ab4975f10a44cf0908f6ce586ad7](https://ropsten.etherscan.io/tx/0xf41868e6b59020965831aac218e1a521b283ab4975f10a44cf0908f6ce586ad7) 132 | 133 | ## Card List 134 | 135 | See [CARD.md](./dapp/CARD.md) for more information. 136 | 137 | ## Contributing 138 | 139 | See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to help out. 140 | 141 | ## Licence 142 | 143 | See [LICENSE](./LICENSE) for details. 144 | -------------------------------------------------------------------------------- /README_zhTW.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

14 | 15 | 16 | > Non-fungible token game 17 | 18 | 19 | 20 | 這是一款加密卡牌遊戲,使用[OpenZepplin](https://github.com/OpenZeppelin/openzeppelin-solidity)。 21 | 在ERC-721 non-fungible token 標準下建置合約,透過truffle框架編寫及部署合約,合約部署之後,使用者可以透過前端來跟dApp互動,再由dApp呼叫部署在以太坊上的合約來進行遊戲。 22 | 23 | ## 遊戲方式 24 | 25 | #### 卡牌收集 26 | 玩家先使用ether來兌換卡牌,每張卡牌上面有不一樣的點數。 27 | 28 | ![Card Collection](herocollection.gif) 29 | 30 | #### 卡牌對戰 31 | 擁有卡牌之後可以開始對戰,對戰開始之前玩家必須先選擇自己要出的卡牌,之後系統會隨機決定此回合是比大或是比小,並且隨機產生點數,玩家獲勝之後可獲得獎勵。 32 | 33 | ![Card Battle](cardbattle.gif) 34 | 35 | #### 對戰紀錄 36 | 對戰之後可以透過對戰紀錄看到所有過去的遊戲紀錄,包含遊戲的勝負以及下注的金額。 37 | 38 | ![Game History](gamehistory.gif) 39 | 40 | ## ERC-721 Token 41 | 42 | ERC-721 non-fungible token: 43 | 符合這合約的每個token都是不一樣的,擁有獨一無二的token ID,與ERC-20相比,ERC-20的token可以彼此互換的,使用者A的50個token與使用者B的50個token是沒有差別的,但如果是ERC-721的話因為每個token ID都不一樣,所以不可以互換,視為獨立的資產。 44 | 45 | ## Contracts 46 | 合約內容存放在`contracts/`底下: 47 | - `CryptoHerosGame.sol`: 48 | 遊戲實際執行方式、規則實作在此合約內 49 | - `CryptoHerosToken.sol`: 50 | 卡牌購買、產生方法實作在此合約內 51 | 52 | ## Technical stack 53 | 54 | ### Frontend 55 | - React 56 | - Redux 57 | - Saga 58 | - Web3(MetaMask) 59 | 60 | ### UI 61 | - Sass 62 | - Material-UI 63 | 64 | ### Smart contract/Solidity 65 | - Truffle 66 | 67 | ### Test environment/Private chain 68 | - ganache 69 | 70 | ## Requirements 71 | 72 | * NodeJS 8.0以上. 73 | * Windows, Linux 或 Mac OS X. 74 | 75 | ## How To Install Dependencies 76 | 77 | 先安裝所需的相關套件: 78 | 79 | 會需要在local端起一個以太坊的節點,推薦使用 `ganache-cli`,你可以透過 npm install來安裝。 80 | 81 | ``` 82 | npm install -g ganache-cli 83 | ``` 84 | 85 | 安裝truffle: 86 | 87 | ``` 88 | npm install -g truffle 89 | ``` 90 | 91 | 安裝其餘所需的套件: 92 | 93 | ``` 94 | npm install 95 | ``` 96 | 97 | ## How To Test 98 | 99 | 先確定local端的以太坊節點有成功啟動. 執行: 100 | 101 | ``` 102 | ganache-cli --gasLimit 0xffffffffff -p 8545 103 | ``` 104 | 105 | 然後編譯並且部署合約: 106 | 107 | ``` 108 | truffle compile && truffle migrate 109 | ``` 110 | 111 | 測試合約: 112 | 113 | ``` 114 | truffle test 115 | ``` 116 | 117 | ## Playground 118 | 119 | 我們已經將合約部署至 [Ropsten](https://ropsten.etherscan.io/) 測試鏈上。 你可以直接對合約進行操作。 120 | 121 | | Contract | Token address | Transaction hash 122 | |------------------|---------------|--------------------- 123 | | CryptoHerosGame | [0xb4FF27d8cD1C5b1e3D4BD8A8FFEBdA9BE9517a4b](https://ropsten.etherscan.io/address/0xb4ff27d8cd1c5b1e3d4bd8a8ffebda9be9517a4b) | [0x49bb8698e2951a0c7eb091038b500694cdf37c74ec51d6c98d91823dc9595b95](https://ropsten.etherscan.io/tx/0x49bb8698e2951a0c7eb091038b500694cdf37c74ec51d6c98d91823dc9595b95) 124 | | CryptoHerosToken | [0xa82Bc392bF65d03A796E1666d27594fB31De4B93](https://ropsten.etherscan.io/address/0xa82bc392bf65d03a796e1666d27594fb31de4b93) | [0xf41868e6b59020965831aac218e1a521b283ab4975f10a44cf0908f6ce586ad7](https://ropsten.etherscan.io/tx/0xf41868e6b59020965831aac218e1a521b283ab4975f10a44cf0908f6ce586ad7) 125 | 126 | ## Card List 127 | 128 | 查看我們的卡牌列表 [CARD.md](./dapp/CARD.md). 129 | 130 | ## Contributing 131 | 132 | 查看幫助我們的方法 [CONTRIBUTING.md](./CONTRIBUTING.md). 133 | 134 | ## Licence 135 | 136 | 查看 [LICENSE](./LICENSE) . 137 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | - [ ] Check the owner total owned 4 | - [ ] Link to IPFS show the image 5 | - [ ] Transfer Ownership test component 6 | - [ ] Update CryptoHero information -------------------------------------------------------------------------------- /cardbattle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/cardbattle.gif -------------------------------------------------------------------------------- /contracts/LowBit.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.12; 2 | 3 | import 'zeppelin-solidity/contracts/ownership/Ownable.sol'; 4 | import './LOLYAT.sol'; 5 | 6 | contract LowBit is Ownable { 7 | 8 | uint constant gameFee = 0.005 ether; 9 | uint constant minPrice = 0.01 ether; 10 | uint constant minHerosToken = 5 ether; 11 | 12 | //address public LowBit = 0x0; 13 | uint256 public maxSingleGameId = 0; 14 | 15 | uint nonce = 0; 16 | LOLYAT LOLYAT; 17 | 18 | struct SingleGame { 19 | address player; 20 | uint256 userResult; 21 | uint256 contractResult; 22 | uint256 playerBet; 23 | uint8 game; // 0: smaller. 1: greater 24 | uint8 result; // 0 user win, 1 contract win, 2 draw 25 | } 26 | 27 | SingleGame[] public singleGames; 28 | 29 | mapping(address => uint256[]) public usersSingleGames; 30 | 31 | constructor(LOLYAT _LOLYAT) public { 32 | LOLYAT = _LOLYAT; 33 | } 34 | 35 | function () payable public { 36 | 37 | } 38 | 39 | function createSingleGame(uint _tokenId) payable public returns (uint256) { 40 | require(msg.value >= minPrice); 41 | require(address(this).balance >= minHerosToken); 42 | require(LOLYAT.ownerOf(_tokenId) == msg.sender); 43 | 44 | uint userTokenNumber; 45 | uint contractTokenNumber; 46 | (userTokenNumber, , ,) = LOLYAT.getTokenProperty(_tokenId); 47 | (contractTokenNumber, , ,) = LOLYAT.getTokenProperty(rand(0, LOLYAT.getHerosLength())); 48 | 49 | int result; 50 | uint8 game = uint8(rand(0, 2)); 51 | if (game > 0) { 52 | result = int(userTokenNumber - contractTokenNumber); 53 | } else { 54 | result = int(contractTokenNumber - userTokenNumber); 55 | } 56 | 57 | SingleGame memory _singleGame; 58 | if (result == 0) { 59 | _singleGame = SingleGame({player: msg.sender, userResult: userTokenNumber, contractResult: contractTokenNumber, playerBet: msg.value, game: game, result: 2}); 60 | require(msg.sender.send(msg.value * 1 - gameFee)); 61 | 62 | } else if (result > 0) { 63 | _singleGame = SingleGame({player: msg.sender, userResult: userTokenNumber, contractResult: contractTokenNumber, playerBet: msg.value, game: game, result: 0}); 64 | require(msg.sender.send(msg.value * 150 / 100)); 65 | 66 | } else { 67 | _singleGame = SingleGame({player: msg.sender, userResult: userTokenNumber, contractResult: contractTokenNumber, playerBet: msg.value, game: game, result: 1}); 68 | } 69 | 70 | maxSingleGameId = singleGames.push(_singleGame) - 1; 71 | 72 | uint256[] userSingleGames = usersSingleGames[msg.sender]; 73 | userSingleGames.push(maxSingleGameId); 74 | 75 | return maxSingleGameId; 76 | } 77 | 78 | // function readUserGamesCount(address _address, uint _idx) public returns (uint){ 79 | // return usersSingleGames[_address][_idx].length; 80 | // } 81 | 82 | function getUserSingleGames(address _address) external view returns (uint256[]) { 83 | return usersSingleGames[_address]; 84 | } 85 | 86 | function rand(uint min, uint max) private returns (uint){ 87 | nonce++; 88 | return uint(sha3(nonce))%(min+max)-min; 89 | } 90 | 91 | function withdraw(uint amount) public payable onlyOwner returns(bool) { 92 | require(amount <= address(this).balance); 93 | owner.transfer(amount); 94 | return true; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 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 | constructor() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /dapp/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "stage-2", 4 | "react", 5 | [ 6 | "env", { 7 | "targets": { 8 | "browsers": ["> 1%", "last 5 versions", "Firefox >= 45", "iOS >=8", "Safari >=8","ie >= 10"] 9 | } 10 | } 11 | ] 12 | ], 13 | "plugins": [ 14 | "babel-plugin-transform-class-properties", 15 | "transform-async-to-generator", 16 | "transform-export-extensions", 17 | "add-module-exports", 18 | [ 19 | "transform-runtime", { "polyfill": false, "regenerator": true } 20 | ] 21 | ] 22 | } 23 | 24 | -------------------------------------------------------------------------------- /dapp/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | -------------------------------------------------------------------------------- /dapp/CARD.md: -------------------------------------------------------------------------------- 1 | # Card 2 | 3 | > Card List 4 | 5 | ## Number Card List 6 | Name | IPFS Hash 7 | --------------------------|:------------------------------------------------ 8 | 1.png | QmNbPeXSeUEg6oEhRVFa5uSwVdi8GbXewttkVKf3zX2oyX 9 | 2.png | QmaJUZLbFN4D3HkjEWbUrUqJXtFfjEjVcbauE3YSh393ht 10 | 3.png | QmSdjH5q4Y3h8Uv6knNL4wybgi5Kxrxbni335Y5ooMtKjg 11 | 4.png | Qmdi4p96LLjrmgoj5pJRSLnorJZ4bzdddyrN2jjLM6fXke 12 | 5.png | QmNnvqXtomFAiM34aJRd2rTimCyQnxeemHTLZWwxTGXeWa 13 | 6.png | QmTMig9fwhU77oaWKMHYvMbb3b1wyWviWUxEeYHBdH2T9f 14 | 7.png | QmQXDnUzPSfrQWLcc4vXeVyZquW8cPN4b3qU8zcpsf6ZDP 15 | 8.png | QmQ8Hg8aXtqET4Apd6AGDLmeZTsi9JVvZnMdRGsvdYBgkx 16 | 9.png | QmWQ3oxfnXzpw35wiGnrc2HNV3U6iT4eT6VpoEtLwdKUsT 17 | 10.png | QmUHsb1T1NuEL1TDQMD1dENpZFYP9NsnLo2iohKR3ydz2t 18 | 11.png | QmaQc91N7YvZ8NaxeQfe9xpiVErTH5EJqTiyWVjf6CnV9X 19 | 12.png | QmYtQprzsas3NzA7v4i8y1PJf89bMdr3bUqEqLgXkmfN6X 20 | 13.png | Qmcsu3ATk6cwhXSi2nhkeZqJc6Urk9o6sWX5ZapMDLhedA 21 | 14.png | QmWCk6YgA13WZkF8exa1atTAFx6nzNaYbyYf2jZQRTEJyL 22 | 15.png | QmVDYWJs9RNxX75agGWmKTyLrsaFEJqfFqEcDMVbg9Ftnq 23 | 16.png | Qmd9Xyuf3zQiyPfjDisVwL6J4AcTJy4ycFWBXdCQmjupyk 24 | 17.png | QmZPkZq2XjPVa1oWWLiVDii7okohRrUw1CuzJvicHwUsCa 25 | 18.png | QmZguKxTcU6wpGoz9MUfy5nN4EHMFABHejUrTdjou2hJ1M 26 | 19.png | QmWzdBNu1ikXvcXo7C1WyUk3FxWRnDo5gt2WKm14Rcs1Pc 27 | 20.png | Qmf8MPrUF41e5N5rtXVEGZg5AC7m8NLsjqf9ad9fvwVSrw 28 | 29 | ## Image Card List 30 | Name | IPFS Hash 31 | --------------------------|:------------------------------------------------ 32 | Pixel1.png | QmeARz345pVTS1CgT8YWrTiMnj8wk56nKBcbxEgSvVjpXE 33 | Pixel2.png | QmRmKvVZVPSAiFCXsa214mxBBibZqJM6aJWvmA8ndkc5ig 34 | Pixel3.png | QmRqYud4pr7gqJTX9YJYmwE9Xy7EkPt4bAz8mJUPKgvk3X 35 | Pixel4.png | QmcKZeiZg4Q1m8X1vxAGCWMYhn113ufT31UiQrfC51Jkdd 36 | Pixel5.png | QmRGeBTZroqhAdKkr47SKosECB8yJGTYrU8ckUdvBxDyu5 37 | Pixel6.png | QmS8zmqAcVwQ7eUSY1QawnLd3sbUAVgjpwTaUcSAtpEDR9 38 | Pixel7.png | QmYXFq6sLRN5iPUaRsG5neSHZMuQXaFHAg8LGKna3yvPiU 39 | Pixel8.png | QmT3G9RzgvXqtXnF79sVseUnJERvU9UA7o43RQh1wdfMPR 40 | Pixel9.png | QmbrWUkysFbt1AKZskPSeeVWYTPJevfiqtEN9DWy5gftkU 41 | Pixel10.png | QmeGxiwH7uvKMpwJ8673udXx7UbExVFmhVrAWBpM7uYyXD 42 | Pixel11.png | QmZjyyqjKL5cibcGhHvP5kp5cnptNy5z4RJ4ecUGkEjCHE 43 | Pixel12.png | QmZQ6xv21HYsJTVM9zxaGW6AGHE7rFFdVDF1euCsKXdcPG 44 | Pixel13.png | QmaKWivznExeXocW5hPWrFAhab1rdpiHw7c1WdqsvFT8Xc 45 | Pixel14.png | Qmey9MiEUWLSd4Kfr4LPj476z5pQ53CNJxxAqR6UmMCofQ 46 | Pixel15.png | Qmf79rRMGGUaoccybavoMXWkoZ2wUNKxwkPhHe7TjjWBXM 47 | Pixel16.png | QmVALBXYymSKPz5wN1JFVHrZmnNhz7JW8J8QM5zVrHmagk 48 | Pixel17.png | QmRUvsn4j65tn3GgynWDTEq88VGxnXkN2S5GvT5PEHo1hP 49 | Pixel18.png | QmXhPFrZHQRXmicJtNigkV3oCB23KTK8rwHJDKQrkGk39k 50 | Pixel19.png | QmVWd4RQqg1k6PqggZUY8tRafyZhwMZAZc1Qavpfdb3QZs 51 | Pixel20.png | QmfZ9111VoCssYnCRFjyKRRTaqE638Dys5duDLNqDUr7ZV 52 | 53 | ## Background Card List 54 | Name | IPFS Hash 55 | --------------------------|:------------------------------------------------ 56 | bg1.png | Qmb9jK8bPGaWAVm46kxDMr2Hf1YWcNGiGRhxoRiTRvfNeW 57 | bg2.png | QmeJgkdZWnkwPuyhXkoKJEiKHq7h3cBazn8BsLiiWyeGm4 58 | bg3.png | QmZNUkkQrZ199WZEDuftsKCzfoYESor9fBmotjgYHrkGbx 59 | bg4.png | QmVJ68pmy84r58gbKyYBzt7AjDwUaCqFk7guEYqm9Wr6NL 60 | bg5.png | QmRgiejrcQaMx9vmma3YebrdPqyxwUasXTqToYjyHCLhPC 61 | bg6.png | QmTNtVi21mQuVpqsyY2qFDSi6jktvA9FZGp4Kjbyskaesc 62 | bg7.png | QmPgxySdWktfwUdP2kFdisnaW4HVyWjykKYZn1vmvzhmXu 63 | bg8.png | QmTU3PkpEhJA2AyartCkVSEmJJtCwo9qvVMg4CJi2a4DGJ 64 | bg9.png | QmNuY4DVZcouMJhWj99GyPVZGKpCe8caN7WpSKkjyU1wZc 65 | bg10.png | QmPmT4tc5hSLgmiJzjk92kNHpWGuaDHjQL6eWhpmwzpwVA 66 | bg11.png | QmRzxZLAt6iJ5ijNEALwug3kfxFxCSxQbvy4a7ucco4ZXC 67 | bg12.png | QmTcM5VyR1ceY31k2u1tYqqen1WzNGehoBcYNLJHePqQc3 68 | bg13.png | QmTZGi4Ee6oEgcTsHR72kbL1jrkpWe8YfZ7dmxMyKb42GY 69 | bg14.png | QmTVEnLKPYTBhZy3AAwpiVRP6fKiTxdF2426P3ri4nNpjH 70 | bg15.png | QmP9YNnJ5HresHPyKXuEViSS58iDGyBQDCC4NhxnXdTsAn 71 | bg16.png | QmTDfdUwLNTXJ1PgRqPxyW41jrdxhvh72C4h62dNhNgvtP 72 | bg17.png | QmPAa1joAw1MZGNyGxKpS7bwUXY3ty8eyXAhZBaGYV749c 73 | bg18.png | QmNUiebZznyunT7wD7Vydn87G9BVXQFYp8Gpk5odATiZey 74 | bg19.png | QmaMGoBBP79cAo3T5HzSS1qfA3HFi5S8PPd5JsFtwM58ud 75 | bg20.png | QmchyB9nScjWKdN5sVfZaRSPiEMirtMBbMkzkvBfVDPj9m 76 | -------------------------------------------------------------------------------- /dapp/README.md: -------------------------------------------------------------------------------- 1 | # Nifty game dapp 2 | 3 | This is the CryptoHeros dapp. 4 | 5 | ### Technical stack 6 | 7 | #### Frontend 8 | - React 9 | - Redux 10 | - Saga 11 | - Web3(MetaMask) 12 | 13 | #### UI 14 | - Sass 15 | - Material-UI 16 | 17 | #### Smart contract/Solidity 18 | - Truffle 19 | 20 | ### Install flow 21 | 22 | #### Install ganache 23 | 24 | ``` 25 | npm install -g ganache-cli 26 | ``` 27 | 28 | #### Install truffle 29 | 30 | ``` 31 | npm install -g truffle 32 | ``` 33 | 34 | #### Build repo 35 | 36 | ``` 37 | npm install 38 | truffle compile 39 | ``` 40 | 41 | #### Start repo 42 | 43 | Open a new console 44 | ``` 45 | ganache-cli 46 | ``` 47 | 48 | ``` 49 | truffle migrate 50 | npm start 51 | ``` 52 | 53 | #### Build repo 54 | 55 | ``` 56 | npm run build 57 | ``` 58 | 59 | ## Reference 60 | ganache-cli: https://github.com/trufflesuite/ganache-cli 61 | truffle: https://github.com/trufflesuite/truffle 62 | -------------------------------------------------------------------------------- /dapp/config/env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | 7 | // Make sure that including paths.js after env.js will read .env variables. 8 | delete require.cache[require.resolve('./paths')]; 9 | 10 | const NODE_ENV = process.env.NODE_ENV; 11 | if (!NODE_ENV) { 12 | throw new Error( 13 | 'The NODE_ENV environment variable is required but was not specified.' 14 | ); 15 | } 16 | 17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 18 | var dotenvFiles = [ 19 | `${paths.dotenv}.${NODE_ENV}.local`, 20 | `${paths.dotenv}.${NODE_ENV}`, 21 | // Don't include `.env.local` for `test` environment 22 | // since normally you expect tests to produce the same 23 | // results for everyone 24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`, 25 | paths.dotenv, 26 | ].filter(Boolean); 27 | 28 | // Load environment variables from .env* files. Suppress warnings using silent 29 | // if this file is missing. dotenv will never modify any environment variables 30 | // that have already been set. 31 | // https://github.com/motdotla/dotenv 32 | dotenvFiles.forEach(dotenvFile => { 33 | if (fs.existsSync(dotenvFile)) { 34 | require('dotenv').config({ 35 | path: dotenvFile, 36 | }); 37 | } 38 | }); 39 | 40 | // We support resolving modules according to `NODE_PATH`. 41 | // This lets you use absolute paths in imports inside large monorepos: 42 | // https://github.com/facebookincubator/create-react-app/issues/253. 43 | // It works similar to `NODE_PATH` in Node itself: 44 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 45 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 46 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 47 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 48 | // We also resolve them to make sure all tools using them work consistently. 49 | const appDirectory = fs.realpathSync(process.cwd()); 50 | process.env.NODE_PATH = (process.env.NODE_PATH || '') 51 | .split(path.delimiter) 52 | .filter(folder => folder && !path.isAbsolute(folder)) 53 | .map(folder => path.resolve(appDirectory, folder)) 54 | .join(path.delimiter); 55 | 56 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 57 | // injected into the application via DefinePlugin in Webpack configuration. 58 | const REACT_APP = /^REACT_APP_/i; 59 | 60 | function getClientEnvironment(publicUrl) { 61 | const raw = Object.keys(process.env) 62 | .filter(key => REACT_APP.test(key)) 63 | .reduce( 64 | (env, key) => { 65 | env[key] = process.env[key]; 66 | return env; 67 | }, 68 | { 69 | // Useful for determining whether we’re running in production mode. 70 | // Most importantly, it switches React into the correct mode. 71 | NODE_ENV: process.env.NODE_ENV || 'development', 72 | // Useful for resolving the correct path to static assets in `public`. 73 | // For example, . 74 | // This should only be used as an escape hatch. Normally you would put 75 | // images into the `src` and `import` them in code to get their paths. 76 | PUBLIC_URL: publicUrl, 77 | } 78 | ); 79 | // Stringify all values so we can feed into Webpack DefinePlugin 80 | const stringified = { 81 | 'process.env': Object.keys(raw).reduce((env, key) => { 82 | env[key] = JSON.stringify(raw[key]); 83 | return env; 84 | }, {}), 85 | }; 86 | 87 | return { raw, stringified }; 88 | } 89 | 90 | module.exports = getClientEnvironment; 91 | -------------------------------------------------------------------------------- /dapp/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /dapp/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /dapp/config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right 24 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | 36 | 39 |
40 | 41 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /dapp/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /dapp/scripts/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'production'; 5 | process.env.NODE_ENV = 'production'; 6 | 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | const path = require('path'); 18 | const chalk = require('chalk'); 19 | const fs = require('fs-extra'); 20 | const webpack = require('webpack'); 21 | const config = require('../config/webpack.config.prod'); 22 | const paths = require('../config/paths'); 23 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 24 | const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); 25 | const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); 26 | const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); 27 | const printBuildError = require('react-dev-utils/printBuildError'); 28 | 29 | const measureFileSizesBeforeBuild = 30 | FileSizeReporter.measureFileSizesBeforeBuild; 31 | const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; 32 | const useYarn = fs.existsSync(paths.yarnLockFile); 33 | 34 | // These sizes are pretty large. We'll warn for bundles exceeding them. 35 | const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; 36 | const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; 37 | 38 | // Warn and crash if required files are missing 39 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 40 | process.exit(1); 41 | } 42 | 43 | // First, read the current file sizes in build directory. 44 | // This lets us display how much they changed later. 45 | measureFileSizesBeforeBuild(paths.appBuild) 46 | .then(previousFileSizes => { 47 | // Remove all content but keep the directory so that 48 | // if you're in it, you don't end up in Trash 49 | fs.emptyDirSync(paths.appBuild); 50 | // Merge with the public folder 51 | copyPublicFolder(); 52 | // Start the webpack build 53 | return build(previousFileSizes); 54 | }) 55 | .then( 56 | ({ stats, previousFileSizes, warnings }) => { 57 | if (warnings.length) { 58 | console.log(chalk.yellow('Compiled with warnings.\n')); 59 | console.log(warnings.join('\n\n')); 60 | console.log( 61 | '\nSearch for the ' + 62 | chalk.underline(chalk.yellow('keywords')) + 63 | ' to learn more about each warning.' 64 | ); 65 | console.log( 66 | 'To ignore, add ' + 67 | chalk.cyan('// eslint-disable-next-line') + 68 | ' to the line before.\n' 69 | ); 70 | } else { 71 | console.log(chalk.green('Compiled successfully.\n')); 72 | } 73 | 74 | console.log('File sizes after gzip:\n'); 75 | printFileSizesAfterBuild( 76 | stats, 77 | previousFileSizes, 78 | paths.appBuild, 79 | WARN_AFTER_BUNDLE_GZIP_SIZE, 80 | WARN_AFTER_CHUNK_GZIP_SIZE 81 | ); 82 | console.log(); 83 | 84 | const appPackage = require(paths.appPackageJson); 85 | const publicUrl = paths.publicUrl; 86 | const publicPath = config.output.publicPath; 87 | const buildFolder = path.relative(process.cwd(), paths.appBuild); 88 | printHostingInstructions( 89 | appPackage, 90 | publicUrl, 91 | publicPath, 92 | buildFolder, 93 | useYarn 94 | ); 95 | }, 96 | err => { 97 | console.log(chalk.red('Failed to compile.\n')); 98 | printBuildError(err); 99 | process.exit(1); 100 | } 101 | ); 102 | 103 | // Create the production build and print the deployment instructions. 104 | function build(previousFileSizes) { 105 | console.log('Creating an optimized production build...'); 106 | 107 | let compiler = webpack(config); 108 | return new Promise((resolve, reject) => { 109 | compiler.run((err, stats) => { 110 | if (err) { 111 | return reject(err); 112 | } 113 | const messages = formatWebpackMessages(stats.toJson({}, true)); 114 | if (messages.errors.length) { 115 | // Only keep the first error. Others are often indicative 116 | // of the same problem, but confuse the reader with noise. 117 | if (messages.errors.length > 1) { 118 | messages.errors.length = 1; 119 | } 120 | return reject(new Error(messages.errors.join('\n\n'))); 121 | } 122 | if ( 123 | process.env.CI && 124 | (typeof process.env.CI !== 'string' || 125 | process.env.CI.toLowerCase() !== 'false') && 126 | messages.warnings.length 127 | ) { 128 | console.log( 129 | chalk.yellow( 130 | '\nTreating warnings as errors because process.env.CI = true.\n' + 131 | 'Most CI servers set it automatically.\n' 132 | ) 133 | ); 134 | return reject(new Error(messages.warnings.join('\n\n'))); 135 | } 136 | return resolve({ 137 | stats, 138 | previousFileSizes, 139 | warnings: messages.warnings, 140 | }); 141 | }); 142 | }); 143 | } 144 | 145 | function copyPublicFolder() { 146 | fs.copySync(paths.appPublic, paths.appBuild, { 147 | dereference: true, 148 | filter: file => file !== paths.appHtml, 149 | }); 150 | } 151 | -------------------------------------------------------------------------------- /dapp/scripts/start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'development'; 5 | process.env.NODE_ENV = 'development'; 6 | 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | const fs = require('fs'); 18 | const chalk = require('chalk'); 19 | const webpack = require('webpack'); 20 | const WebpackDevServer = require('webpack-dev-server'); 21 | const clearConsole = require('react-dev-utils/clearConsole'); 22 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 23 | const { 24 | choosePort, 25 | createCompiler, 26 | prepareProxy, 27 | prepareUrls, 28 | } = require('react-dev-utils/WebpackDevServerUtils'); 29 | const openBrowser = require('react-dev-utils/openBrowser'); 30 | const paths = require('../config/paths'); 31 | const config = require('../config/webpack.config.dev'); 32 | const createDevServerConfig = require('../config/webpackDevServer.config'); 33 | 34 | const useYarn = fs.existsSync(paths.yarnLockFile); 35 | const isInteractive = process.stdout.isTTY; 36 | 37 | // Warn and crash if required files are missing 38 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 39 | process.exit(1); 40 | } 41 | 42 | // Tools like Cloud9 rely on this. 43 | const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; 44 | const HOST = process.env.HOST || '0.0.0.0'; 45 | 46 | // We attempt to use the default port but if it is busy, we offer the user to 47 | // run on a different port. `detect()` Promise resolves to the next free port. 48 | choosePort(HOST, DEFAULT_PORT) 49 | .then(port => { 50 | if (port == null) { 51 | // We have not found a port. 52 | return; 53 | } 54 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; 55 | const appName = require(paths.appPackageJson).name; 56 | const urls = prepareUrls(protocol, HOST, port); 57 | // Create a webpack compiler that is configured with custom messages. 58 | const compiler = createCompiler(webpack, config, appName, urls, useYarn); 59 | // Load proxy config 60 | const proxySetting = require(paths.appPackageJson).proxy; 61 | const proxyConfig = prepareProxy(proxySetting, paths.appPublic); 62 | // Serve webpack assets generated by the compiler over a web sever. 63 | const serverConfig = createDevServerConfig( 64 | proxyConfig, 65 | urls.lanUrlForConfig 66 | ); 67 | const devServer = new WebpackDevServer(compiler, serverConfig); 68 | // Launch WebpackDevServer. 69 | devServer.listen(port, HOST, err => { 70 | if (err) { 71 | return console.log(err); 72 | } 73 | if (isInteractive) { 74 | clearConsole(); 75 | } 76 | console.log(chalk.cyan('Starting the development server...\n')); 77 | openBrowser(urls.localUrlForBrowser); 78 | }); 79 | 80 | ['SIGINT', 'SIGTERM'].forEach(function(sig) { 81 | process.on(sig, function() { 82 | devServer.close(); 83 | process.exit(); 84 | }); 85 | }); 86 | }) 87 | .catch(err => { 88 | if (err && err.message) { 89 | console.log(err.message); 90 | } 91 | process.exit(1); 92 | }); 93 | -------------------------------------------------------------------------------- /dapp/scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | const argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI or in coverage mode 22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 23 | argv.push('--watch'); 24 | } 25 | 26 | 27 | jest.run(argv); 28 | -------------------------------------------------------------------------------- /dapp/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/.DS_Store -------------------------------------------------------------------------------- /dapp/src/actions/LOLYATActions.js: -------------------------------------------------------------------------------- 1 | import * as types from "../constants/actionTypes"; 2 | 3 | export const LOLYATNameAction = (networkId) => ({ 4 | type: types.CRYPTOHEROS_TOKEN_NAME, 5 | networkId, 6 | }); 7 | 8 | export const LOLYATSymbolAction = (networkId) => ({ 9 | type: types.CRYPTOHEROS_TOKEN_SYMBOL, 10 | networkId, 11 | }); 12 | 13 | export const LOLYATTokenURIAction = (networkId, tokenId, callBack) => ({ 14 | type: types.CRYPTOHEROS_TOKEN_TOKEN_URI, 15 | networkId, 16 | tokenId, 17 | callBack, 18 | }); 19 | 20 | export const LOLYATOwnerOfAction = (networkId, tokenId) => ({ 21 | type: types.CRYPTOHEROS_TOKEN_OWNER_OF, 22 | networkId, 23 | tokenId, 24 | }); 25 | 26 | export const LOLYATTransferOwnershipAction = (networkId, address) => ({ 27 | type: types.CRYPTOHEROS_TOKEN_TRANSFER_OWNERSHIP, 28 | networkId, 29 | address, 30 | }); 31 | 32 | export const LOLYATGetOwnedTokensAction = (networkId, address, callBack) => ({ 33 | type: types.CRYPTOHEROS_TOKEN_GET_OWNED_TOKENS, 34 | networkId, 35 | address, 36 | callBack, 37 | }); 38 | -------------------------------------------------------------------------------- /dapp/src/actions/counterActions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/actionTypes'; 2 | 3 | export const addAction = (num) => ({ 4 | type: types.ADD, 5 | num 6 | }); 7 | 8 | export const subAction = (num) => ({ 9 | type: types.SUB, 10 | num 11 | }); -------------------------------------------------------------------------------- /dapp/src/actions/fetchingActions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/actionTypes'; 2 | 3 | export const fetching = () => ({ 4 | type: types.FETCHING 5 | }); 6 | 7 | export const fetchComplete = () => ({ 8 | type: types.FETCH_COMPLETE 9 | }); -------------------------------------------------------------------------------- /dapp/src/actions/healthActions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/actionTypes'; 2 | 3 | export const healthAction = () => ({ 4 | type: types.HEALTH 5 | }); 6 | -------------------------------------------------------------------------------- /dapp/src/actions/metaMaskActions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/actionTypes'; 2 | 3 | export const metaMaskAccountAction = (account) => ({ 4 | type: types.METAMASK_ACCOUNT, 5 | account 6 | }); 7 | 8 | export const metaMaskNetworkAction = (network) => ({ 9 | type: types.METAMASK_NETWORK, 10 | network 11 | }); -------------------------------------------------------------------------------- /dapp/src/actions/simpleTokenActions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/actionTypes'; 2 | 3 | export const simpleTokenNameAction = (networkId) => ({ 4 | type: types.SIMPLE_TOKEN_NAME, 5 | networkId 6 | }); 7 | 8 | export const simpleTokenSymbolAction = (networkId) => ({ 9 | type: types.SIMPLE_TOKEN_SYMBOL, 10 | networkId 11 | }); 12 | 13 | export const simpleTokenDecimalsAction = (networkId) => ({ 14 | type: types.SIMPLE_TOKEN_DECIMALS, 15 | networkId 16 | }); 17 | -------------------------------------------------------------------------------- /dapp/src/actions/warningActions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/actionTypes'; 2 | 3 | export const warningOpenAction = (message) => ({ 4 | type: types.WARNING_OPEN, 5 | message 6 | }); 7 | 8 | export const warningCloseAction = () => ({ 9 | type: types.WARNING_CLOSE 10 | }); -------------------------------------------------------------------------------- /dapp/src/apis/api.http: -------------------------------------------------------------------------------- 1 | ### Server Health 2 | GET http://localhost:3000/v1/health 3 | 4 | -------------------------------------------------------------------------------- /dapp/src/apis/api.js: -------------------------------------------------------------------------------- 1 | 2 | const URI = 'http://localhost:3000/v1'; 3 | 4 | const headers = { 5 | Accept: 'application/json', 6 | 'Content-Type': 'application/json' 7 | }; 8 | 9 | const route = (path, uri= URI) => (`${uri}/${path}`); 10 | 11 | const fetchPOST = async (route, payload) => { 12 | try { 13 | const res = await fetch(route, { 14 | method: 'POST', 15 | headers, 16 | body: JSON.stringify(payload) 17 | }); 18 | 19 | const result = await res.json(); 20 | 21 | if (res.status !== 200) { 22 | throw Error(result.message); 23 | } 24 | 25 | return result; 26 | } catch (error) { 27 | console.log('POST API ERROR: ', route, error); 28 | return error; 29 | } 30 | } 31 | 32 | const fetchGET = async (route, queryParams) => { 33 | try { 34 | let query = null; 35 | if (queryParams !== undefined && queryParams !== null) { 36 | query = Object.keys(queryParams) 37 | .map((key) => (`${key}=${queryParams[key]}`)) 38 | .join('&'); 39 | } 40 | 41 | const uri = (query === null) ? route : `${route}?${query}`; 42 | const res = await fetch(uri); 43 | const result = await res.json(); 44 | 45 | if (res.status !== 200) { 46 | throw Error(result.message); 47 | } 48 | 49 | return result; 50 | } catch (error) { 51 | console.log('GET API ERROR: ', route, error); 52 | return error; 53 | } 54 | } 55 | 56 | export const getHealthResult = (payload) => fetchGET(route('health'), payload); 57 | -------------------------------------------------------------------------------- /dapp/src/components/App.css: -------------------------------------------------------------------------------- 1 | @keyframes cloudMove1 { 2 | to { 3 | left: 10%; } 4 | 50% { 5 | left: 13%; } 6 | form { 7 | left: 10%; } } 8 | 9 | @keyframes cloudMove2 { 10 | to { 11 | right: 13%; } 12 | 50% { 13 | right: 17%; } 14 | form { 15 | right: 13%; } } 16 | 17 | @keyframes starMove1 { 18 | to { 19 | top: 30%; } 20 | 50% { 21 | top: 32%; } 22 | form { 23 | top: 30%; } } 24 | 25 | @keyframes starMove2 { 26 | to { 27 | top: 37%; } 28 | 50% { 29 | top: 35%; } 30 | form { 31 | top: 37%; } } 32 | 33 | @keyframes starMove3 { 34 | to { 35 | top: 75%; } 36 | 50% { 37 | top: 78%; } 38 | form { 39 | top: 75%; } } 40 | 41 | @keyframes ElfMove { 42 | to { 43 | transform: scale(1); } 44 | 50% { 45 | transform: scale(1.5); } 46 | form { 47 | transform: scale(1); } } 48 | 49 | @keyframes loading_coin_breath { 50 | form { 51 | transform: scale(1); } 52 | 50% { 53 | transform: scale(1.1); } 54 | to { 55 | transform: scale(1); } } 56 | 57 | img { 58 | display: block; } 59 | 60 | .App { 61 | position: relative; 62 | width: 100%; 63 | height: 100%; 64 | overflow: hidden; } 65 | .App .index { 66 | position: absolute; 67 | top: 0; 68 | left: 0; 69 | width: 100%; 70 | height: 100%; 71 | display: flex; 72 | justify-content: center; 73 | align-items: center; } 74 | .App .index .titlebox { 75 | position: relative; 76 | display: flex; 77 | align-items: center; 78 | flex-direction: column; 79 | margin-bottom: 80px; } 80 | .App .index .titlebox img.title { 81 | width: 530px; 82 | height: auto; 83 | margin-bottom: 40px; } 84 | .App .index .titlebox img.title2 { 85 | width: 239px; 86 | height: auto; 87 | margin-bottom: 185px; } 88 | .App .index .titlebox a.gameplay { 89 | position: absolute; 90 | bottom: 20%; 91 | display: block; 92 | width: 250px; 93 | height: 40px; 94 | background-image: url("../images/gameplaybtn.png"); 95 | background-repeat: no-repeat; 96 | background-position: center center; 97 | transition: transform .3s; 98 | background-size: 152px 23px; } 99 | .App .index .titlebox a.gameplay:hover { 100 | transform: scale(1.1); } 101 | .App .index .titlebox a.gameplay:before, .App .index .titlebox a.gameplay:after { 102 | position: absolute; 103 | display: block; 104 | width: 47px; 105 | height: 37px; 106 | background-image: url("../images/loveicon.png"); 107 | top: 3px; } 108 | .App .index .titlebox a.gameplay:before { 109 | content: ""; 110 | left: -20px; } 111 | .App .index .titlebox a.gameplay:after { 112 | content: ""; 113 | right: -20px; } 114 | .App .index .titlebox a.getCard { 115 | position: absolute; 116 | bottom: 0%; 117 | display: block; 118 | width: 250px; 119 | height: 40px; 120 | background-image: url("../images/getcard.png"); 121 | background-repeat: no-repeat; 122 | background-position: center center; 123 | transition: transform .3s; 124 | background-size: 132px 19px; } 125 | .App .index .titlebox a.getCard:hover { 126 | transform: scale(1.1); } 127 | .App .index .titlebox a.getCard:before, .App .index .titlebox a.getCard:after { 128 | position: absolute; 129 | display: block; 130 | width: 47px; 131 | height: 37px; 132 | background-image: url("../images/loveicon.png"); 133 | top: 3px; } 134 | .App .index .titlebox a.getCard:before { 135 | content: ""; 136 | left: -20px; } 137 | .App .index .titlebox a.getCard:after { 138 | content: ""; 139 | right: -20px; } 140 | .App .index .titlebox a.showTutorial { 141 | position: absolute; 142 | bottom: -20%; 143 | display: block; 144 | width: 250px; 145 | height: 40px; 146 | background-image: url("../images/Tutorial.png"); 147 | background-repeat: no-repeat; 148 | background-position: center center; 149 | transition: transform .3s; 150 | background-size: 132px 19px; } 151 | .App .index .titlebox a.showTutorial:hover { 152 | transform: scale(1.1); } 153 | .App .index .titlebox a.showTutorial:before, .App .index .titlebox a.showTutorial:after { 154 | position: absolute; 155 | display: block; 156 | width: 47px; 157 | height: 37px; 158 | background-image: url("../images/loveicon.png"); 159 | top: 3px; } 160 | .App .index .titlebox a.showTutorial:before { 161 | content: ""; 162 | left: -20px; } 163 | .App .index .titlebox a.showTutorial:after { 164 | content: ""; 165 | right: -20px; } 166 | .App .index .ladder { 167 | position: absolute; 168 | width: 133px; 169 | height: 274px; 170 | background-image: url("../images/ladder.png"); 171 | bottom: 38px; 172 | left: 75px; } 173 | 174 | span.text { 175 | position: fixed; 176 | display: block; 177 | width: 100%; 178 | height: 30px; 179 | bottom: 60px; 180 | left: 0; } 181 | span.text .footer { 182 | width: 50%; 183 | height: 30px; 184 | margin: 0 auto; 185 | display: flex; 186 | justify-content: space-around; } 187 | span.text .footer > a { 188 | color: #fff; 189 | text-decoration: underline; } 190 | -------------------------------------------------------------------------------- /dapp/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Card from './Card'; 3 | import Arena from './Arena'; 4 | import IndexUi from './IndexUi'; 5 | import Loading from './Loading'; 6 | import Tutorial from './Tutorial'; 7 | import title from "../images/titlelogo.png"; 8 | import title2 from "../images/title2.png"; 9 | import './App.css'; 10 | import { MetaMask } from './MetaMask/MetaMask'; 11 | import { TweenMax } from "gsap/TweenMax"; 12 | import { Warning } from './Warning/Warning'; 13 | import { doGetTokenProperty, doGetOwnedTokens, doMint } from '../lib/cryptoHerosTokenService'; 14 | import { doGetUserSingleGames, getSingleGame, } from '../lib/cryptoHerosGameService'; 15 | 16 | import LoadingCoin from './LoadingCoin'; 17 | import NiftyAlert from "./NiftyAlert"; 18 | 19 | class App extends Component { 20 | state={ 21 | web3: null, 22 | brand: [], 23 | brandItem: [], 24 | isLoading: true, 25 | isGetCardPage: false, 26 | isShowArena: false, 27 | isLoadingCoinLoading: false, 28 | userOwnCards: [], 29 | historyGamesCount: 0, 30 | isErrorOpen: false, 31 | isShowTutorial: false, 32 | errorMessage: '' 33 | } 34 | 35 | constructor(props) { 36 | super(props); 37 | this.setWeb3 = this.setWeb3.bind(this); 38 | } 39 | 40 | handleAlertOpen=(errmag)=>{ 41 | this.setState({isErrorOpen: true, errorMessage: errmag}) 42 | } 43 | 44 | handleAlertClose=()=>{ 45 | this.setState({isErrorOpen: false}) 46 | } 47 | 48 | componentDidMount(){ 49 | window.addEventListener("mousemove", this.Elffn); 50 | let t = setInterval(()=>{ 51 | const {network, account} = this.props.metaMask; 52 | if(network!==null && account!==null){ 53 | window.clearInterval(t); 54 | //抓卡牌編號 55 | this.props.handleCryptoHerosTokenGetOwnedTokens(network, account, this.TimeOutGoTokens); 56 | } 57 | },300); 58 | 59 | } 60 | 61 | componentDidUpdate(prevProps) { 62 | if(this.props.metaMask.account && prevProps.metaMask.account && this.props.metaMask.account !== prevProps.metaMask.account) { 63 | window.location.reload(); 64 | } 65 | } 66 | 67 | setWeb3(web3) { 68 | this.setState({web3}); 69 | } 70 | 71 | Elffn = e =>{ 72 | let wElf = document.querySelector(".Elf0").clientWidth; 73 | let hElf = document.querySelector(".Elf0").clientHeight; 74 | let scaleInt = e.pageX > window.innerWidth / 2 ? -1 : 1; 75 | TweenMax.to(".Elf0", 0.3, 76 | { 77 | left: e.pageX - (wElf / 2 ), 78 | top: e.pageY - (hElf / 2 ), 79 | scaleX: scaleInt 80 | } 81 | ) 82 | } 83 | 84 | closeMyCard = ()=>{ 85 | this.setState({isGetCardPage: false}); 86 | } 87 | 88 | //撈回所有的卡片 89 | TimeOutGoTokens = res =>{ 90 | this.setState({brand: res.split(","), isLoading: false}); 91 | } 92 | 93 | //進入卡牌畫面 94 | gotoAndPlayGame = async () =>{ 95 | this.setState({ 96 | isLoadingCoinLoading: true, 97 | }); 98 | 99 | const { network, account, } = this.props.metaMask; 100 | const result = await doGetOwnedTokens(network, account); 101 | const cardsPromises = result.map(cur => doGetTokenProperty(network, cur.c)); 102 | const brandItem = await Promise.all(cardsPromises); 103 | 104 | this.setState({ 105 | brandItem, 106 | isGetCardPage: true, 107 | isLoadingCoinLoading: false, 108 | }); 109 | 110 | setTimeout(() => { 111 | TweenMax.staggerFrom(".cardBox", 0.5, { 112 | transform: "translateY(-30px)", 113 | opacity:0}, 0.2 114 | ); 115 | }, 0); 116 | } 117 | 118 | // 開局, 前往鬥技場 119 | handleGoArena = async e => { 120 | this.setState({ 121 | isLoadingCoinLoading: true, 122 | }); 123 | 124 | const { network, account, } = this.props.metaMask; 125 | const result = await doGetOwnedTokens(network, account); 126 | 127 | if(result.length === 0) { 128 | this.handleAlertOpen('You have no cards, please get card'); 129 | // alert('You have no cards, please get card'); 130 | this.setState({ 131 | isLoadingCoinLoading: false, 132 | }); 133 | this.gotoAndPlayGame(); 134 | return; 135 | } 136 | 137 | const cardsPromises = result.map(cur => doGetTokenProperty(network, cur.c)); 138 | const detailResult = await Promise.all(cardsPromises); 139 | const userOwnCards = detailResult.map((cur, idx) => { 140 | return ({ 141 | tokenId: result[idx].c[0], 142 | roleImg: cur['1'], 143 | numberImg: cur['3'], 144 | bgImg: cur['2'], 145 | }); 146 | }); 147 | 148 | const games = await doGetUserSingleGames(network, account); 149 | const gamePromises = games.map(cur => getSingleGame(network, cur.c[0], account)); 150 | const gameDetails = await Promise.all(gamePromises); 151 | const historyGames = gameDetails.map(game => { 152 | return ({ 153 | userBet: game[3].c[0] / 10000, 154 | isWin: game[5].c[0], 155 | }); 156 | }); 157 | 158 | this.setState({ 159 | isLoadingCoinLoading: false, 160 | isShowArena: true, 161 | historyGamesCount: historyGames.length, 162 | userOwnCards, 163 | }); 164 | } 165 | 166 | // 從鬥技場回到首頁 167 | handleBackFromArena = e => { 168 | this.setState({ 169 | isShowArena: false, 170 | }); 171 | } 172 | 173 | handleOpenLoadingCoin = e => { 174 | this.setState({ 175 | isLoadingCoinLoading: true, 176 | }); 177 | } 178 | 179 | handleCloseLoadingCoin = e => { 180 | this.setState({ 181 | isLoadingCoinLoading: false, 182 | }); 183 | } 184 | 185 | // 開啟教戰手冊 186 | handleOpenTutorial = e => { 187 | this.setState({ 188 | isShowTutorial: true, 189 | }); 190 | } 191 | 192 | // 關閉教戰手冊 193 | handleCloseTutorial = e => { 194 | this.setState({ 195 | isShowTutorial: false, 196 | }); 197 | } 198 | 199 | render() { 200 | const { 201 | userOwnCards, isLoading, brandItem, isGetCardPage, 202 | isShowArena, isLoadingCoinLoading, historyGamesCount, 203 | isShowTutorial, 204 | } = this.state; 205 | 206 | return ( 207 |
208 |
209 |
210 | 211 | 212 | { isLoading && } 213 | { !isLoading && } 214 | { !isLoading && } 215 | 216 |
217 | 218 | 219 | 220 | {this.state.isErrorOpen && 221 | 226 | } 227 |
228 | 229 | 239 | 240 | 250 | 251 | { isShowTutorial && } 252 | { isLoadingCoinLoading && } 253 | 254 | 255 | 256 | 261 | 262 |
263 | ); 264 | } 265 | } 266 | 267 | export default App; 268 | -------------------------------------------------------------------------------- /dapp/src/components/App.scss: -------------------------------------------------------------------------------- 1 | @import "./_mixinKeyframes.scss"; 2 | img{ 3 | display: block; 4 | } 5 | .App{ 6 | position: relative; 7 | width: 100%; 8 | height: 100%; 9 | overflow: hidden; 10 | .index{ 11 | position: absolute; 12 | top: 0; 13 | left: 0; 14 | width: 100%; 15 | height: 100%; 16 | 17 | display: flex; 18 | justify-content: center; 19 | align-items: center; 20 | .titlebox{ 21 | position: relative; 22 | display: flex; 23 | align-items: center; 24 | flex-direction: column; 25 | 26 | margin-bottom: 80px; 27 | img.title{ 28 | width: 530px; 29 | height: auto; 30 | margin-bottom: 40px; 31 | } 32 | img.title2{ 33 | width: 239px; 34 | height: auto; 35 | margin-bottom: 185px; 36 | } 37 | a.gameplay{ 38 | @include GameBtn("../images/gameplaybtn.png", 20%); 39 | background-size: 152px 23px; 40 | } 41 | a.getCard{ 42 | @include GameBtn("../images/getcard.png", 0%); 43 | background-size: 132px 19px; 44 | } 45 | 46 | a.showTutorial{ 47 | @include GameBtn("../images/Tutorial.png", -20%); 48 | background-size: 132px 19px; 49 | } 50 | } 51 | 52 | 53 | .ladder{ 54 | position: absolute; 55 | width: 133px; 56 | height: 274px; 57 | background-image: url("../images/ladder.png"); 58 | bottom: 38px; 59 | left: 75px; 60 | } 61 | } 62 | } 63 | 64 | span.text{ 65 | position: fixed; 66 | display: block; 67 | width: 100%; 68 | height: 30px; 69 | bottom: 60px; 70 | left: 0; 71 | .footer{ 72 | width: 50%; 73 | height: 30px; 74 | margin: 0 auto; 75 | display: flex; 76 | justify-content: space-around; 77 | >a{ 78 | color: #fff; 79 | text-decoration: underline; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /dapp/src/components/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | // TODO consider about redux 7 | const div = document.createElement('div'); 8 | //ReactDOM.render(, div); 9 | }); 10 | -------------------------------------------------------------------------------- /dapp/src/components/BattleCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames/bind'; 3 | import style from './style.css'; 4 | const cx = classnames.bind(style); 5 | 6 | export default class extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | isOpenCard: props.isOpenCard, 11 | isLock: props.isLock || false, 12 | }; 13 | } 14 | 15 | componentDidMount() { 16 | // 若鎖定牌卡則禁止翻牌 17 | if (this.state.isLock) { 18 | return; 19 | } 20 | window.setTimeout(() => { 21 | this.setState({ 22 | isOpenCard: !this.state.isOpenCard 23 | }); 24 | }, 1000); 25 | } 26 | 27 | handleOpenCard = () => { 28 | // 若鎖定牌卡則禁止翻牌 29 | if (this.state.isLock) { 30 | return; 31 | } 32 | 33 | this.setState({ 34 | isOpenCard: !this.state.isOpenCard 35 | }); 36 | } 37 | 38 | render() { 39 | const { bgImg, pixelImg, numberImg, isSmall, } = this.props; 40 | const { isOpenCard, } = this.state; 41 | 42 | return ( 43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /dapp/src/components/BattleCard/style.css: -------------------------------------------------------------------------------- 1 | .cardbox { 2 | display: inline-block; 3 | perspective: 800px; } 4 | .cardbox.small { 5 | transform: scale(0.8); } 6 | .cardbox .gameCard { 7 | position: relative; 8 | width: 288px; 9 | height: 407px; 10 | transition: all 0.5s linear; 11 | transform-style: preserve-3d; } 12 | .cardbox .gameCard.OpenCard { 13 | transform: rotateY(180deg); } 14 | .cardbox .gameCard .seeCard { 15 | position: absolute; 16 | width: 100%; 17 | height: 100%; 18 | top: 0; 19 | left: 0; } 20 | .cardbox .gameCard .seeCard > div { 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | width: 100%; 25 | height: 100%; 26 | background-size: 100% 100%; 27 | backface-visibility: hidden; 28 | transform: rotateY(180deg); } 29 | .cardbox .gameCard .seeCard > div.c_bg { 30 | z-index: 1; } 31 | .cardbox .gameCard .seeCard > div.c_user { 32 | z-index: 2; } 33 | .cardbox .gameCard .seeCard > div.c_number { 34 | z-index: 3; } 35 | .cardbox .gameCard .BackCard { 36 | position: absolute; 37 | width: 100%; 38 | height: 100%; 39 | top: 0; 40 | left: 0; 41 | background-image: url("../../images/back.png"); 42 | background-size: 100% 100%; 43 | backface-visibility: hidden; } 44 | -------------------------------------------------------------------------------- /dapp/src/components/BattleCard/style.scss: -------------------------------------------------------------------------------- 1 | .cardbox{ 2 | display: inline-block; 3 | perspective: 800px; 4 | 5 | &.small { 6 | transform: scale(.8); 7 | } 8 | 9 | .gameCard { 10 | position: relative; 11 | width: 288px; 12 | height: 407px; 13 | transition: all 0.5s linear; 14 | transform-style: preserve-3d; 15 | 16 | &.OpenCard { 17 | transform: rotateY(180deg); 18 | } 19 | 20 | & .seeCard { 21 | position: absolute; 22 | width: 100%; 23 | height: 100%; 24 | top: 0; 25 | left: 0; 26 | 27 | > div{ 28 | position: absolute; 29 | top: 0; 30 | left: 0; 31 | width: 100%; 32 | height: 100%; 33 | background-size: 100% 100%; 34 | backface-visibility: hidden; 35 | transform: rotateY(180deg); 36 | &.c_bg{ 37 | z-index: 1; 38 | } 39 | &.c_user{ 40 | z-index: 2; 41 | } 42 | &.c_number{ 43 | z-index: 3; 44 | } 45 | } 46 | } 47 | 48 | & .BackCard{ 49 | position: absolute; 50 | width: 100%; 51 | height: 100%; 52 | top: 0; 53 | left: 0; 54 | background-image: url("../../images/back.png"); 55 | background-size: 100% 100%; 56 | backface-visibility: hidden; 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /dapp/src/components/Card/Card.css: -------------------------------------------------------------------------------- 1 | @keyframes btnSetAmin { 2 | to { 3 | transform: scale(1); } 4 | 50% { 5 | transform: scale(1.4); } 6 | form { 7 | transform: scale(1); } } 8 | 9 | .Card { 10 | position: absolute; 11 | top: 0; 12 | left: 100%; 13 | width: 100%; 14 | height: 100%; 15 | overflow: auto; 16 | background-color: #272424; 17 | transition: left .3s; } 18 | .Card .ui { 19 | position: absolute; } 20 | .Card .ui.start1 { 21 | width: 13px; 22 | height: 20px; 23 | background-image: url("../../images/star1.png"); 24 | top: 30%; 25 | left: 10%; 26 | animation: starMove1 3s infinite linear; } 27 | .Card .ui.start2 { 28 | width: 13px; 29 | height: 21px; 30 | background-image: url("../../images/star2.png"); 31 | top: 37%; 32 | right: 5%; 33 | animation: starMove2 2s infinite linear; } 34 | .Card .ui.start3 { 35 | width: 13px; 36 | height: 20px; 37 | background-image: url("../../images/star1.png"); 38 | top: 75%; 39 | left: 5%; 40 | animation: starMove3 4s infinite linear; } 41 | .Card .ui.Elf1 { 42 | bottom: 10%; 43 | left: 120px; 44 | width: 26px; 45 | height: 25px; 46 | background-image: url("../../images/Elf1.png"); 47 | animation: ElfMove 2s infinite linear; } 48 | .Card .ui.Elf2 { 49 | top: 15%; 50 | left: 25%; 51 | width: 26px; 52 | height: 25px; 53 | background-image: url("../../images/Elf2.png"); 54 | animation: ElfMove 2s infinite linear; } 55 | .Card .ui.Elf3 { 56 | bottom: 15%; 57 | right: 8%; 58 | width: 26px; 59 | height: 25px; 60 | background-image: url("../../images/Elf3.png"); 61 | animation: ElfMove 2s infinite linear; } 62 | .Card .cardtitle { 63 | display: block; 64 | width: 481px; 65 | height: auto; 66 | margin: 90px auto 0 auto; } 67 | .Card .cardtitle > img { 68 | width: 100%; 69 | height: auto; 70 | margin-bottom: 50px; } 71 | .Card .cardtitle .btn_box { 72 | width: 481px; } 73 | .Card .cardtitle .btn_box > a { 74 | display: block; 75 | width: 50%; 76 | height: 25px; 77 | background-repeat: no-repeat; 78 | background-position: center; } 79 | .Card .cardtitle .btn_box a.goback { 80 | background-image: url("../../images/goBack.png"); 81 | background-size: 151px 25px; 82 | float: left; } 83 | .Card .cardtitle .btn_box a.getHero { 84 | background-image: url("../../images/getherobtn.png"); 85 | background-size: 165px 25px; 86 | float: right; } 87 | .Card.open { 88 | left: 0; } 89 | .Card .cloud_card1 { 90 | position: absolute; 91 | width: 68px; 92 | height: 27px; 93 | background-image: url("../../images/cloud.png"); 94 | top: 12%; 95 | left: 16%; 96 | animation: cloudMove1 18s infinite linear; } 97 | .Card .cloud_card2 { 98 | position: absolute; 99 | width: 68px; 100 | height: 27px; 101 | background-image: url("../../images/cloud.png"); 102 | top: 19%; 103 | right: 12%; 104 | animation: cloudMove2 18s infinite linear; } 105 | .Card .c_mid { 106 | width: 970px; 107 | height: auto; 108 | margin: 0 auto; 109 | padding-top: 80px; } 110 | .Card .c_mid .addCardBtn { 111 | width: 25%; 112 | height: 282px; 113 | overflow: hidden; 114 | display: inline-block; 115 | margin-bottom: 40px; } 116 | .Card .c_mid .addCardBtn > a { 117 | display: block; 118 | cursor: pointer; 119 | position: relative; 120 | width: 200px; 121 | height: 282px; 122 | margin: 0 auto; 123 | background-size: 100% 100%; 124 | background-image: url("../../images/addcard.png"); 125 | background-size: 100% 100%; 126 | opacity: 0.4; 127 | transition: opacity .2s; } 128 | .Card .c_mid .addCardBtn > a:hover { 129 | opacity: 0.6; } 130 | .Card .c_mid .cardBox { 131 | width: 25%; 132 | height: auto; 133 | overflow: hidden; 134 | display: inline-block; 135 | margin-bottom: 40px; } 136 | .Card .c_mid .cardBox .cardbg { 137 | cursor: pointer; 138 | position: relative; 139 | width: 200px; 140 | height: 282px; 141 | margin: 0 auto; 142 | background-size: 100% 100%; } 143 | .Card .c_mid .cardBox .cardbg > div { 144 | position: absolute; 145 | width: 100%; 146 | height: 100%; 147 | background-size: 100% 100%; 148 | top: 0; 149 | left: 0; } 150 | .Card .c_mid .cardBox .cardbg > div.s_bgcard { 151 | z-index: 1; } 152 | .Card .c_mid .cardBox .cardbg > div.s_user { 153 | z-index: 2; } 154 | .Card .c_mid .cardBox .cardbg > div.s_number { 155 | z-index: 3; } 156 | -------------------------------------------------------------------------------- /dapp/src/components/Card/Card.scss: -------------------------------------------------------------------------------- 1 | @keyframes btnSetAmin { 2 | to { transform: scale(1);} 3 | 50% { transform: scale(1.4); } 4 | form { transform: scale(1);} 5 | } 6 | .Card{ 7 | position: absolute; 8 | top: 0; 9 | left: 100%; 10 | width: 100%; 11 | height: 100%; 12 | overflow: auto; 13 | background-color: #272424; 14 | transition: left .3s; 15 | .ui{ 16 | position: absolute; 17 | &.start1{ 18 | width: 13px; 19 | height: 20px; 20 | background-image: url("../../images/star1.png"); 21 | top: 30%; 22 | left: 10%; 23 | animation: starMove1 3s infinite linear; 24 | } 25 | &.start2{ 26 | width: 13px; 27 | height: 21px; 28 | background-image: url("../../images/star2.png"); 29 | top: 37%; 30 | right: 5%; 31 | animation: starMove2 2s infinite linear; 32 | } 33 | &.start3{ 34 | width: 13px; 35 | height: 20px; 36 | background-image: url("../../images/star1.png"); 37 | top: 75%; 38 | left: 5%; 39 | animation: starMove3 4s infinite linear; 40 | } 41 | &.Elf1{ 42 | bottom: 10%; 43 | left: 120px; 44 | width: 26px; 45 | height: 25px; 46 | background-image: url("../../images/Elf1.png"); 47 | animation: ElfMove 2s infinite linear; 48 | } 49 | &.Elf2{ 50 | top: 15%; 51 | left: 25%; 52 | width: 26px; 53 | height: 25px; 54 | background-image: url("../../images/Elf2.png"); 55 | animation: ElfMove 2s infinite linear; 56 | } 57 | &.Elf3{ 58 | bottom: 15%; 59 | right: 8%; 60 | width: 26px; 61 | height: 25px; 62 | background-image: url("../../images/Elf3.png"); 63 | animation: ElfMove 2s infinite linear; 64 | } 65 | } 66 | .cardtitle{ 67 | display: block; 68 | width: 481px; 69 | height: auto; 70 | margin: 90px auto 0 auto; 71 | >img{ 72 | width: 100%; 73 | height: auto; 74 | margin-bottom: 50px; 75 | } 76 | .btn_box{ 77 | width: 481px; 78 | >a{ 79 | display: block; 80 | width: 50%; 81 | height: 25px; 82 | background-repeat: no-repeat; 83 | background-position: center; 84 | } 85 | a.goback{ 86 | background-image: url("../../images/goBack.png"); 87 | background-size: 151px 25px; 88 | float: left; 89 | } 90 | a.getHero{ 91 | background-image: url("../../images/getherobtn.png"); 92 | background-size: 165px 25px; 93 | float: right; 94 | } 95 | } 96 | } 97 | &.open{ 98 | left: 0; 99 | } 100 | .cloud_card1{ 101 | position: absolute; 102 | width: 68px; 103 | height: 27px; 104 | background-image: url("../../images/cloud.png"); 105 | top: 12%; 106 | left: 16%; 107 | animation: cloudMove1 18s infinite linear; 108 | } 109 | .cloud_card2{ 110 | position: absolute; 111 | width: 68px; 112 | height: 27px; 113 | background-image: url("../../images/cloud.png"); 114 | top: 19%; 115 | right: 12%; 116 | animation: cloudMove2 18s infinite linear; 117 | } 118 | .c_mid{ 119 | width: 970px; 120 | height: auto; 121 | margin: 0 auto; 122 | padding-top: 80px; 123 | .addCardBtn{ 124 | width: 25%; 125 | height: 282px; 126 | overflow: hidden; 127 | display: inline-block; 128 | margin-bottom: 40px; 129 | >a{ 130 | display: block; 131 | cursor: pointer; 132 | position: relative; 133 | width: 200px; 134 | height: 282px; 135 | margin: 0 auto; 136 | background-size: 100% 100%; 137 | background-image: url("../../images/addcard.png"); 138 | background-size: 100% 100%; 139 | opacity: 0.4; 140 | transition: opacity .2s; 141 | &:hover{ 142 | opacity: 0.6; 143 | } 144 | } 145 | } 146 | .cardBox{ 147 | width: 25%; 148 | height: auto; 149 | overflow: hidden; 150 | display: inline-block; 151 | margin-bottom: 40px; 152 | .cardbg{ 153 | cursor: pointer; 154 | position: relative; 155 | width: 200px; 156 | height: 282px; 157 | margin: 0 auto; 158 | background-size: 100% 100%; 159 | >div{ 160 | position: absolute; 161 | width: 100%; 162 | height: 100%; 163 | background-size: 100% 100%; 164 | top: 0; 165 | left: 0; 166 | &.s_bgcard{z-index: 1;} 167 | &.s_user{z-index: 2;} 168 | &.s_number{z-index: 3;} 169 | } 170 | } 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /dapp/src/components/Card/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import classnames from 'classnames/bind'; 3 | import cardtitle from '../../images/cardtitle.png'; 4 | import style from './Card.css'; 5 | import { getLOLYATAddress } from '../../lib/web3Service'; 6 | import axios from 'axios'; 7 | import LoadingCoin from '../LoadingCoin'; 8 | import Button from 'material-ui/Button'; 9 | 10 | import Dialog, { 11 | DialogActions, 12 | DialogContent, 13 | DialogContentText, 14 | } from 'material-ui/Dialog'; 15 | 16 | const cx = classnames.bind(style); 17 | 18 | 19 | function ipfsUrl(hash) { 20 | return 'https://ipfs.infura.io/ipfs/' + hash; 21 | } 22 | 23 | const ErrorAlertDialog = (props) => ( 24 | 30 | 31 | 32 | {props.errmsg} 33 | 34 | 35 | 36 | 39 | 40 | 41 | ); 42 | 43 | class Card extends Component { 44 | 45 | state = { 46 | doMintTx: '', 47 | isLoading: false, 48 | isOpenAlert: false, 49 | errmsg: '', 50 | } 51 | 52 | handleClickAlertOpen = () => { 53 | this.setState({ isOpenAlert: true }); 54 | } 55 | 56 | handleAlertClose = () => { 57 | this.setState({ isOpenAlert: false }); 58 | } 59 | 60 | CreateHero = async() =>{ 61 | const{doMint} = this.props; 62 | const result = await doMint(); 63 | this.setState({'doMintTx': result, isLoading: true},()=>{ 64 | this.handleSubmitMetaMask(this.state.doMintTx); 65 | }) 66 | } 67 | 68 | handleSubmitMetaMask =(doMintTx)=>{ 69 | const {account, network} = this.props.metaMask; 70 | const {web3} = this.props; 71 | 72 | const msk = { 73 | from: account, 74 | to: getLOLYATAddress(network), 75 | value: this.props.web3.toWei(0.01, 'ether'), 76 | data: doMintTx 77 | } 78 | 79 | web3.eth.sendTransaction(msk, this.handleMetaMaskCallBack); 80 | 81 | } 82 | 83 | handleMetaMaskCallBack = (err, result)=>{ 84 | 85 | if(err) { 86 | this.setState({errmsg: 'Sorry, transaction failed'}, ()=> this.setState({isOpenAlert: true})); 87 | console.error('MetaMask Error:', err.message); 88 | this.setState({isLoading: false}); 89 | return; 90 | } 91 | 92 | const tx = result; 93 | let t = setInterval(async ()=>{ 94 | const result = await axios.get(`https://api-ropsten.etherscan.io/api?module=transaction&action=gettxreceiptstatus&txhash=${tx}&apikey=RAADZVN65BQA7G839DFN3VHWCZBQMRBR11`); 95 | 96 | if(result.data.result.status === "1") { 97 | this.ReloadDataFn(); 98 | window.clearInterval(t); 99 | } 100 | 101 | },3000); 102 | } 103 | 104 | ReloadDataFn =()=>{ 105 | const {network, account} = this.props.metaMask; 106 | //抓卡牌編號 107 | this.props.handleCryptoHerosTokenGetOwnedTokens(network, account, this.props.TimeOutGoTokens); 108 | setTimeout(() => { 109 | this.setState({isLoading: false},()=> this.props.gotoAndPlayGame()); 110 | }, 6000); 111 | } 112 | 113 | render() { 114 | const {brandItem, isGetCardPage, closeMyCard} = this.props; 115 | 116 | const alertMsg = this.state.isOpenAlert && 117 | ; 121 | 122 | return ( 123 |
124 | 125 |
126 |
127 | 128 |
129 |
130 |
131 |
132 |
133 |
134 | 135 |
136 | 137 |
138 | 139 | 140 |
141 |
142 | 143 |
144 | { 145 | brandItem.map((obj, idx)=>{ 146 | return ( 147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | ) 155 | }) 156 | } 157 |
158 | 159 |
160 |
161 | { 162 | this.state.isLoading && 163 | } 164 | { alertMsg } 165 |
166 | ); 167 | } 168 | } 169 | export default Card; 170 | -------------------------------------------------------------------------------- /dapp/src/components/Contract.css: -------------------------------------------------------------------------------- 1 | .Contract { 2 | position: absolute; 3 | background-color: #fff; 4 | z-index: 9999; } 5 | -------------------------------------------------------------------------------- /dapp/src/components/Contract.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { LinearProgress } from 'material-ui/Progress'; 3 | import './Contract.css'; 4 | export default class Contract extends Component { 5 | // 將 read contract method 透過 redux 儲存與 saga 非同步呼叫,將 web3 當成 API 使用 6 | render() { 7 | const name = this.props.LOLYAT &&

{this.props.LOLYAT.name}

; 8 | const symbol = this.props.LOLYAT &&

{this.props.LOLYAT.symbol}

; 9 | const ownedToken = this.props.cryptoHerosOwned &&

{this.props.cryptoHerosOwned}

; 10 | const tokenURI = this.props.cryptoHerosOwnedTokenURI && 11 |
12 | 13 |

{this.props.cryptoHerosOwnedTokenURI.name}

14 |

{this.props.cryptoHerosOwnedTokenURI.HP}

15 |

{this.props.cryptoHerosOwnedTokenURI.ATK}

16 |

{this.props.cryptoHerosOwnedTokenURI.DEF}

17 |
; 18 | const progress = this.props.isFetching && 19 | return ( 20 |
21 |

Contract

22 |
{progress}
23 | { 24 | /* 25 | 26 | 27 | */ 28 | } 29 | 30 | 31 | 32 | 33 |
34 | {ownedToken} 35 |
36 |
37 | ); 38 | } 39 | } -------------------------------------------------------------------------------- /dapp/src/components/Contract.scss: -------------------------------------------------------------------------------- 1 | .Contract{ 2 | position: absolute; 3 | background-color: #fff; 4 | z-index: 9999; 5 | } -------------------------------------------------------------------------------- /dapp/src/components/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const Counter = (props) => ( 4 |
5 |

Counter: {props.count}

6 | 7 | 8 |
9 | ); -------------------------------------------------------------------------------- /dapp/src/components/Health.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const Health = (props) => ( 4 |
5 |

Health

6 |

{props.health}

7 | 8 |
9 | ); -------------------------------------------------------------------------------- /dapp/src/components/IndexUi.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from '../images/logo.png'; 3 | import './indexUi.css'; 4 | export default class className extends Component{ 5 | state={ 6 | eggidx: 0, 7 | } 8 | render() { 9 | return ( 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 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | ) 43 | } 44 | } -------------------------------------------------------------------------------- /dapp/src/components/Loading/Loading.css: -------------------------------------------------------------------------------- 1 | @keyframes LoadAmin { 2 | to { 3 | transform: scale(1); 4 | opacity: 0.5; } 5 | 50% { 6 | transform: scale(1.5); 7 | opacity: 1; } 8 | form { 9 | transform: scale(1); 10 | opacity: 0.5; } } 11 | 12 | .loading { 13 | animation: LoadAmin 1s infinite linear; 14 | opacity: 0.5; } 15 | -------------------------------------------------------------------------------- /dapp/src/components/Loading/Loading.scss: -------------------------------------------------------------------------------- 1 | @keyframes LoadAmin{ 2 | to { transform: scale(1); opacity: 0.5; } 3 | 50% { transform: scale(1.5); opacity: 1;} 4 | form { transform: scale(1); opacity: 0.5; } 5 | } 6 | 7 | .loading{ 8 | animation: LoadAmin 1s infinite linear; 9 | opacity: 0.5; 10 | } -------------------------------------------------------------------------------- /dapp/src/components/Loading/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './Loading.css'; 3 | import loading from "../../images/loading.png"; 4 | export default class extends Component{ 5 | render() { 6 | return ( 7 |
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /dapp/src/components/LoadingCoin/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames/bind'; 3 | import style from './style.css'; 4 | import loadingcoin from '../../images/loadingicoin.png'; 5 | const cx = classnames.bind(style); 6 | 7 | export default class extends React.PureComponent { 8 | render() { 9 | return ( 10 |
11 | 12 |
13 | ) 14 | } 15 | } -------------------------------------------------------------------------------- /dapp/src/components/LoadingCoin/style.css: -------------------------------------------------------------------------------- 1 | .loading-coin-container { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | width: 100%; 9 | height: 100vh; 10 | background-color: rgba(51, 51, 51, 0.9); } 11 | .loading-coin-container img { 12 | display: inline-block; 13 | width: 302px; 14 | height: auto; 15 | animation: loading_coin_breath 2s infinite linear; } 16 | -------------------------------------------------------------------------------- /dapp/src/components/LoadingCoin/style.scss: -------------------------------------------------------------------------------- 1 | .loading-coin-container { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | width: 100%; 9 | height: 100vh; 10 | background-color: rgba(51, 51, 51, 0.9); 11 | 12 | 13 | & img { 14 | display: inline-block; 15 | width: 302px; 16 | height: auto; 17 | 18 | animation: loading_coin_breath 2s infinite linear; 19 | } 20 | } -------------------------------------------------------------------------------- /dapp/src/components/MetaMask/MetaMask.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import Web3 from 'web3'; 3 | import Button from 'material-ui/Button'; 4 | import Dialog, { 5 | DialogActions, 6 | DialogContent, 7 | DialogContentText, 8 | DialogTitle, 9 | } from 'material-ui/Dialog'; 10 | import Slide from 'material-ui/transitions/Slide'; 11 | 12 | const messages = { 13 | 'LOAD_MATAMASK_WALLET_ERROR': 'Load metamask wallet error, maybe try Metamask later, or upload a wallet json file.', 14 | 'EMPTY_METAMASK_ACCOUNT': 'You can choose one MetaMask wallet by unlocking it', 15 | 'METAMASK_ACCOUNT': 'You have choosen the MetamMask Wallet: ', 16 | 'NETWORK_ERROR': 'Network error, please check it.', 17 | 'METAMASK_NOT_INSTALL': 'You must install MetaMask before start.', 18 | 'METAMASK_TEST_NET': 'Our game is available on Ropsten Test Network only. Please switch via MetaMask!' 19 | }; 20 | 21 | const MetaMaskInstallDialog = (props) => ( 22 | 26 | {"Oops, you haven't installed MetaMask"} 27 | 28 | 29 | {"What is MetaMask? "} 30 | Link 31 | 32 | 33 | 34 | 37 | 40 | 41 | 42 | ); 43 | 44 | const MetaMaskLockDialog = (props) => ( 45 | 49 | {"Oops, your MetaMask is locked"} 50 | 51 | 52 | {props.message} 53 | 54 | 55 | 56 | 59 | 60 | 61 | ); 62 | 63 | export class MetaMask extends Component { 64 | constructor(props) { 65 | super(props); 66 | this.state = { 67 | message: '', 68 | metaMaskInstallDialogOpen: false, 69 | metaMaskLockDialogOpen: false, 70 | disableDialog: false 71 | }; 72 | this.handleMetaMaskInstallDialogClose = this.handleMetaMaskInstallDialogClose.bind(this); 73 | this.handleMetaMaskLockDialogClose = this.handleMetaMaskLockDialogClose.bind(this); 74 | } 75 | 76 | fetchWeb3() { 77 | let web3 = window.web3; 78 | if (typeof web3 === 'undefined') { 79 | this.props.setWeb3(null); 80 | this.setState({message: messages.METAMASK_NOT_INSTALL}); 81 | } 82 | } 83 | 84 | fetchAccounts() { 85 | //const { web3 } = window; 86 | if (this.props.web3 !== null) { 87 | this.props.web3.eth.getAccounts((err, accounts) => { 88 | if (err) { 89 | this.setState({message: messages.LOAD_MATAMASK_WALLET_ERROR}); 90 | } else { 91 | if (accounts.length === 0) { 92 | this.props.handleMetaMaskAccount(null); 93 | this.setState({metaMaskLockDialogOpen: true, message: messages.EMPTY_METAMASK_ACCOUNT}) 94 | } else { 95 | // if account changed then change redux state 96 | if (accounts[0] !== this.props.metaMask.account) { 97 | this.props.handleMetaMaskAccount(accounts[0]); 98 | } 99 | } 100 | } 101 | }); 102 | } 103 | } 104 | 105 | fetchNetwork() { 106 | //const { web3 } = window; 107 | if (this.props.web3 !== null) { 108 | this.props.web3.version.getNetwork((err, netId) => { 109 | console.log('netId:',netId); 110 | if(netId === "1"){ 111 | this.props.handleMetaMaskNetwork(null); 112 | this.setState({metaMaskLockDialogOpen: true, message:messages.METAMASK_TEST_NET }); 113 | } 114 | 115 | 116 | if (err) { 117 | this.props.handleMetaMaskNetwork(null); 118 | this.setState({metaMaskLockDialogOpen: true, message: messages.NETWORK_ERROR }); 119 | } else { 120 | // if network changed then change redux state 121 | if (netId !== this.props.metaMask.network) { 122 | this.props.handleMetaMaskNetwork(netId); 123 | } 124 | } 125 | }); 126 | } 127 | } 128 | 129 | componentDidMount() { 130 | let self = this; 131 | window.addEventListener('load', function() { 132 | let web3 = window.web3; 133 | if (typeof web3 !== 'undefined') { 134 | window.web3 = new Web3(web3.currentProvider); 135 | self.props.setWeb3(window.web3); 136 | self.fetchAccounts(); 137 | self.fetchNetwork(); 138 | self.Web3Interval = setInterval(() => self.fetchWeb3(), 1000); 139 | self.AccountInterval = setInterval(() => self.fetchAccounts(), 1000); 140 | self.NetworkInterval = setInterval(() => self.fetchNetwork(), 1000); 141 | } else { 142 | self.setState({metaMaskInstallDialogOpen: true, message: messages.METAMASK_NOT_INSTALL}); 143 | } 144 | }) 145 | } 146 | 147 | componentWillUnmount() { 148 | clearInterval(this.Web3Interval); 149 | clearInterval(this.AccountInterval); 150 | clearInterval(this.NetworkInterval); 151 | } 152 | 153 | handleMetaMaskInstallDialogClose() { 154 | this.setState({metaMaskInstallDialogOpen: false, disableDialog: true}); 155 | } 156 | 157 | handleMetaMaskLockDialogClose() { 158 | this.setState({metaMaskLockDialogOpen: false, disableDialog: true}); 159 | } 160 | 161 | render() { 162 | const metaMaskInstall = this.state.disableDialog === false && 163 | ; 167 | 168 | const metaMaskLock = this.state.disableDialog === false && 169 | 173 | 174 | return ( 175 |
176 | {metaMaskInstall} 177 | {metaMaskLock} 178 |
179 | ); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /dapp/src/components/NiftyAlert/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Button from 'material-ui/Button'; 3 | import Dialog, { 4 | DialogActions, 5 | DialogContent, 6 | DialogContentText, 7 | } from 'material-ui/Dialog'; 8 | 9 | export default class extends Component{ 10 | 11 | 12 | /* 13 | * 14 | * isOpenAlert 開啟pop的狀態 : bool 15 | * handleAlertClose 關閉狀態 : function 16 | * errorMessage 要顯示的 Error 訊息 : String 17 | * 18 | */ 19 | 20 | render() { 21 | const {isOpenAlert, handleAlertClose, errorMessage } = this.props; 22 | return ( 23 | 29 | 30 | 31 | {errorMessage} 32 | 33 | 34 | 35 | 38 | 39 | 40 | ) 41 | } 42 | } -------------------------------------------------------------------------------- /dapp/src/components/SendTransaction.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class SendTransaction extends Component { 4 | constructor(props) { 5 | super(props); 6 | 7 | this.handleSubmit = this.handleSubmit.bind(this); 8 | } 9 | 10 | handleSubmit() { 11 | let self = this; 12 | this.props.web3.eth.sendTransaction({ 13 | from: this.props.metaMask.account, 14 | to: this.props.metaMask.account, 15 | value: this.props.web3.toWei(1, 'ether'), 16 | data: 'dead' 17 | }, function(err, result) { 18 | if (err) { 19 | self.props.handleWarningOpen(err.message); 20 | } else { 21 | self.props.handleWarningOpen(result); 22 | } 23 | }); 24 | } 25 | 26 | render() { 27 | return ( 28 |
29 |

Send Transaction

30 |
To: {this.props.metaMask.account}
31 |
Network: {this.props.metaMask.network}
32 | 33 |
34 | ); 35 | } 36 | } 37 | 38 | export default SendTransaction; -------------------------------------------------------------------------------- /dapp/src/components/Top/Title.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from 'material-ui/Typography'; 3 | 4 | export const Title = (props) => ( 5 | 6 | React, Truffle, MetaMask 7 | 8 | ); -------------------------------------------------------------------------------- /dapp/src/components/Top/Top.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AppBar from 'material-ui/AppBar'; 3 | import Toolbar from 'material-ui/Toolbar'; 4 | import {Title} from './Title'; 5 | 6 | export const Top = (props) => ( 7 |
8 | 9 | 10 | 11 | </Toolbar> 12 | </AppBar> 13 | </div> 14 | ); -------------------------------------------------------------------------------- /dapp/src/components/Tutorial/Tutorial.css: -------------------------------------------------------------------------------- 1 | .tutorial-container { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | width: 100%; 9 | height: 100vh; } 10 | .tutorial-container .content { 11 | position: relative; 12 | padding: 50px 4%; 13 | width: 50%; 14 | height: 80%; 15 | overflow-y: auto; 16 | background-color: rgba(0, 0, 0, 0.7); 17 | border-radius: 20px; } 18 | .tutorial-container .headline, .tutorial-container h2, .tutorial-container h3, .tutorial-container p, .tutorial-container a { 19 | color: #fff; } 20 | .tutorial-container .headline { 21 | font-size: 34px; 22 | font-weight: 600; 23 | text-align: center; } 24 | .tutorial-container h2 { 25 | font-size: 18px; 26 | font-weight: 600; 27 | margin-top: 20px; 28 | margin-bottom: 10px; } 29 | .tutorial-container h3 { 30 | margin-top: 20px; 31 | font-size: 16px; 32 | font-weight: 600; } 33 | .tutorial-container p { 34 | line-height: 1.8; 35 | font-size: 12px; } 36 | .tutorial-container p.warning { 37 | margin-top: 10px; 38 | margin-bottom: 10px; 39 | font-size: 14px; 40 | font-weight: 600; 41 | color: red; } 42 | .tutorial-container a.close { 43 | position: absolute; 44 | top: 25px; 45 | right: 25px; } 46 | .tutorial-container a.close img { 47 | width: 25px; 48 | height: 25px; } 49 | -------------------------------------------------------------------------------- /dapp/src/components/Tutorial/Tutorial.scss: -------------------------------------------------------------------------------- 1 | .tutorial-container { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | width: 100%; 9 | height: 100vh; 10 | // background-color: rgba(#000, .5); 11 | 12 | & .content { 13 | position: relative; 14 | padding: 50px 4%; 15 | width: 50%; 16 | height: 80%; 17 | overflow-y: auto; 18 | background-color: rgba($color: #000, $alpha: 0.7); 19 | border-radius: 20px; 20 | } 21 | 22 | & .headline ,& h2, & h3, & p, & a{ 23 | color: #fff; 24 | } 25 | & .headline { 26 | font-size: 34px; 27 | font-weight: 600; 28 | text-align: center; 29 | } 30 | & h2 { 31 | font-size: 18px; 32 | font-weight: 600; 33 | margin-top: 20px; 34 | margin-bottom: 10px; 35 | } 36 | 37 | & h3 { 38 | margin-top: 20px; 39 | font-size: 16px; 40 | font-weight: 600; 41 | } 42 | 43 | & p { 44 | line-height: 1.8; 45 | font-size: 12px; 46 | 47 | &.warning { 48 | margin-top: 10px; 49 | margin-bottom: 10px; 50 | font-size: 14px; 51 | font-weight: 600; 52 | color: red; 53 | } 54 | } 55 | 56 | & a.close { 57 | position: absolute; 58 | top: 25px; 59 | right: 25px; 60 | 61 | & img { 62 | width: 25px; 63 | height: 25px; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /dapp/src/components/Tutorial/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames/bind'; 3 | import style from './Tutorial.css'; 4 | const cx = classnames.bind(style); 5 | 6 | export default class extends React.Component { 7 | render() { 8 | const { onClose, } = this.props; 9 | 10 | return ( 11 | <div className={cx('tutorial-container')}> 12 | <div className={cx('content')}> 13 | <h1 className={cx('headline')}>Big and Small Card Game tutorial</h1> 14 | 15 | <h2>Introduction</h2> 16 | <p> 17 | Big and Small is a card game that is built under ERC-721 (NFT, non-fungible token) standard with truffle framework. 18 | Each card in the game is a ERC-721, so every card is unique under a specific ownership. 19 | </p> 20 | 21 | 22 | <h2>Getting started</h2> 23 | <p>First, you will need MetaMask to run the game. To download MetaMask, click <a href="https://metamask.io/" target="_blank">here</a></p> 24 | 25 | <p> 26 | Once you downloaded MetaMask, create a wallet so you can receive testnet ETH later on. See <a href="http://bit.ly/bigsmalltutorialM" target="_blank">tutorial</a>. 27 | </p> 28 | 29 | <p> 30 | Go to <a href="http://game.portal.network" target="_blank">Big and Small</a> and switch to Ropsten Test Network via your MetaMask. 31 | </p> 32 | 33 | <p>After switching to Ropsten Test Network, click BUY and receive Test Faucet. Simply request 1 ether from faucet and you will receive within few minutes.</p> 34 | 35 | 36 | <p className={cx('warning')}>***You may also play ‘Big and Small’ on IPFS, completely decentralized. Visit <a href="//game.portalnetworkweb.eth" target="_blank">game.portalnetworkweb.eth</a>***</p> 37 | 38 | <p className={cx('warning')}>You will need Portal Network extension to help you redirect this BNS (blockchain name service). <a href="https://chrome.google.com/webstore/detail/portal-network/apcnffelpkinnpoapmokieojaffmcpmf" target="_blank">Download now</a>.</p> 39 | 40 | 41 | <h2>How to play the game</h2> 42 | <h3>Acquiring the card</h3> 43 | <p>Now everything is ready. To play the game, acquire a ERC-721 card first, which will cost you 0.01 ETH. Once you sign a transaction and send it to our smart contract, you will receive a card with a random number on it. The number will be used to determine the winner later in the battle.</p> 44 | 45 | <h3>Card battle</h3> 46 | <p>Once you acquired a card, you can compete with our bot. </p> 47 | <p>Choose a card and place a bet to play for the round. The bet should be between 0.01 to 1 ETH.</p> 48 | <p>Each round, the smart contract will randomly decide either card with larger number or smaller number wins. At the same time, the smart contract will also randomly generate a number in order to compete with the player.</p> 49 | <p>Depending on the round, card with larger or smaller number will be the winner.</p> 50 | <p>Winner of game will win the 150% of the bet put in.</p> 51 | 52 | <p>You can view our smart contract at our <a href="https://github.com/PortalNetwork/nifty-game" target="_blank">github</a></p> 53 | 54 | <a className={cx('close')} onClick={onClose}> 55 | <img src="https://ipfs.infura.io/ipfs/QmRu3VNTA3HxgHpkqA7SVxsk2JjC96yD1Yse8rJA6NoDjw" alt="close tutorial" /> 56 | </a> 57 | </div> 58 | </div> 59 | ) 60 | } 61 | } -------------------------------------------------------------------------------- /dapp/src/components/Warning/Warning.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Snackbar from 'material-ui/Snackbar'; 3 | 4 | export const Warning = (props) => ( 5 | <Snackbar 6 | anchorOrigin={{ 7 | vertical: 'top', 8 | horizontal: 'right', 9 | }} 10 | autoHideDuration={6000} 11 | open={props.warning.open} 12 | onRequestClose={props.handleWarningClose} 13 | SnackbarContentProps={{ 14 | 'aria-describedby': 'message-id', 15 | }} 16 | message={<span id="message-id">{props.warning.message}</span>} 17 | /> 18 | ) 19 | 20 | export default Warning; -------------------------------------------------------------------------------- /dapp/src/components/_mixinKeyframes.scss: -------------------------------------------------------------------------------- 1 | @keyframes cloudMove1 { 2 | to { left: 10%; } 3 | 50% { left: 13%; } 4 | form { left: 10%; } 5 | } 6 | 7 | @keyframes cloudMove2 { 8 | to { right: 13%; } 9 | 50% { right: 17%; } 10 | form { right: 13%; } 11 | } 12 | 13 | @keyframes starMove1 { 14 | to { top: 30%; } 15 | 50% { top: 32%; } 16 | form { top: 30%; } 17 | } 18 | 19 | @keyframes starMove2 { 20 | to { top: 37%; } 21 | 50% { top: 35%; } 22 | form { top: 37%; } 23 | } 24 | 25 | @keyframes starMove3 { 26 | to { top: 75%; } 27 | 50% { top: 78%; } 28 | form { top: 75%; } 29 | } 30 | 31 | @keyframes ElfMove{ 32 | to { transform: scale(1); } 33 | 50% { transform: scale(1.5); } 34 | form { transform: scale(1); } 35 | } 36 | 37 | @keyframes loading_coin_breath{ 38 | form { transform: scale(1); } 39 | 50% { transform: scale(1.1); } 40 | to { transform: scale(1); } 41 | } 42 | 43 | 44 | @mixin GameBtn($url, $bottom){ 45 | position: absolute; 46 | bottom: $bottom; 47 | display: block; 48 | width: 250px; 49 | height: 40px; 50 | background-image: url($url); 51 | background-repeat: no-repeat; 52 | background-position: center center; 53 | transition: transform .3s; 54 | &:hover{ 55 | transform: scale(1.1); 56 | } 57 | &:before, &:after{ 58 | position: absolute; 59 | display: block; 60 | width: 47px; 61 | height: 37px; 62 | background-image: url("../images/loveicon.png"); 63 | top: 3px; 64 | } 65 | &:before{ 66 | content:""; 67 | left: -20px; 68 | } 69 | &:after{ 70 | content:""; 71 | right: -20px 72 | } 73 | } -------------------------------------------------------------------------------- /dapp/src/components/indexUi.css: -------------------------------------------------------------------------------- 1 | @keyframes cloudMove1 { 2 | to { 3 | left: 10%; } 4 | 50% { 5 | left: 13%; } 6 | form { 7 | left: 10%; } } 8 | 9 | @keyframes cloudMove2 { 10 | to { 11 | right: 13%; } 12 | 50% { 13 | right: 17%; } 14 | form { 15 | right: 13%; } } 16 | 17 | @keyframes starMove1 { 18 | to { 19 | top: 30%; } 20 | 50% { 21 | top: 32%; } 22 | form { 23 | top: 30%; } } 24 | 25 | @keyframes starMove2 { 26 | to { 27 | top: 37%; } 28 | 50% { 29 | top: 35%; } 30 | form { 31 | top: 37%; } } 32 | 33 | @keyframes starMove3 { 34 | to { 35 | top: 75%; } 36 | 50% { 37 | top: 78%; } 38 | form { 39 | top: 75%; } } 40 | 41 | @keyframes ElfMove { 42 | to { 43 | transform: scale(1); } 44 | 50% { 45 | transform: scale(1.5); } 46 | form { 47 | transform: scale(1); } } 48 | 49 | @keyframes loading_coin_breath { 50 | form { 51 | transform: scale(1); } 52 | 50% { 53 | transform: scale(1.1); } 54 | to { 55 | transform: scale(1); } } 56 | 57 | .ui_item a.logo { 58 | display: block; 59 | position: absolute; 60 | width: 90px; 61 | height: 47px; 62 | top: 2%; 63 | left: 2%; } 64 | .ui_item a.logo > img { 65 | width: 100%; } 66 | 67 | .ui_item .linkBOx { 68 | position: absolute; 69 | width: 200px; 70 | height: auto; 71 | top: 20px; 72 | right: 10px; 73 | display: flex; 74 | justify-content: space-around; 75 | align-items: center; } 76 | .ui_item .linkBOx a.linkIcon { 77 | display: block; 78 | width: 50px; 79 | height: 50px; 80 | background-size: 100% 100%; 81 | animation: loading_coin_breath 1s infinite linear; 82 | color: #fff; } 83 | 84 | .ui_item .ui { 85 | display: block; 86 | position: absolute; } 87 | .ui_item .ui.footer { 88 | width: 100%; 89 | height: 107px; 90 | bottom: 0; 91 | left: 0; 92 | background-image: url("../images/footer.png"); 93 | background-position: center; } 94 | .ui_item .ui.tree1 { 95 | width: 155px; 96 | height: 288px; 97 | background-image: url("https://ipfs.infura.io/ipfs/Qmck3FzLLHkypSerue1BBzQkawhN2YhWQSnwFzgdd5E8Jo"); 98 | left: -50px; 99 | bottom: 0; } 100 | .ui_item .ui.tree2 { 101 | width: 300px; 102 | height: 577px; 103 | background-image: url("https://ipfs.infura.io/ipfs/QmUPEBPCySpCjqsCvmePvgmh4gvQZG5bYMnRjShnYMwb72"); 104 | right: -65px; 105 | bottom: 0; } 106 | .ui_item .ui.cloud { 107 | top: 20%; 108 | left: 10%; 109 | width: 68px; 110 | height: 27px; 111 | background-image: url("../images/cloud.png"); 112 | animation: cloudMove1 18s infinite linear; } 113 | .ui_item .ui.cloud2 { 114 | top: 50%; 115 | right: 13%; 116 | width: 68px; 117 | height: 27px; 118 | background-image: url("../images/cloud.png"); 119 | animation: cloudMove2 10s infinite linear; } 120 | .ui_item .ui.start1 { 121 | width: 13px; 122 | height: 20px; 123 | background-image: url("../images/star1.png"); 124 | top: 30%; 125 | left: 20%; 126 | animation: starMove1 3s infinite linear; } 127 | .ui_item .ui.start2 { 128 | width: 13px; 129 | height: 21px; 130 | background-image: url("../images/star2.png"); 131 | top: 37%; 132 | right: 26%; 133 | animation: starMove2 2s infinite linear; } 134 | .ui_item .ui.start3 { 135 | width: 13px; 136 | height: 20px; 137 | background-image: url("../images/star1.png"); 138 | top: 75%; 139 | left: 30%; 140 | animation: starMove3 4s infinite linear; } 141 | .ui_item .ui.Elf0 { 142 | pointer-events: none; 143 | top: 0; 144 | left: 0; 145 | width: 38px; 146 | height: 41px; 147 | background-image: url("../images/Elf0.png"); } 148 | .ui_item .ui.Elf1 { 149 | bottom: 39%; 150 | left: 120px; 151 | width: 26px; 152 | height: 25px; 153 | background-image: url("../images/Elf1.png"); 154 | animation: ElfMove 2s infinite linear; } 155 | .ui_item .ui.Elf2 { 156 | top: 15%; 157 | left: 25%; 158 | width: 26px; 159 | height: 25px; 160 | background-image: url("../images/Elf2.png"); 161 | animation: ElfMove 2s infinite linear; } 162 | .ui_item .ui.Elf3 { 163 | bottom: 15%; 164 | right: 25%; 165 | width: 26px; 166 | height: 25px; 167 | background-image: url("../images/Elf3.png"); 168 | animation: ElfMove 2s infinite linear; } 169 | .ui_item .ui.fruit1 { 170 | bottom: 10%; 171 | left: 20%; 172 | width: 26px; 173 | height: 31px; 174 | background-image: url("../images/fruit1.png"); 175 | animation: ElfMove 2s infinite linear; } 176 | .ui_item .ui.fruit2 { 177 | top: 25%; 178 | right: 7%; 179 | width: 22px; 180 | height: 33px; 181 | background-image: url("../images/fruit2.png"); 182 | animation: ElfMove 2s infinite linear; } 183 | -------------------------------------------------------------------------------- /dapp/src/components/indexUi.scss: -------------------------------------------------------------------------------- 1 | @import "./_mixinKeyframes.scss"; 2 | .ui_item{ 3 | a.logo{ 4 | display: block; 5 | position: absolute; 6 | width: 90px; 7 | height: 47px; 8 | top: 2%; 9 | left: 2%; 10 | >img{ 11 | width: 100%; 12 | } 13 | } 14 | 15 | .linkBOx{ 16 | position: absolute; 17 | width: 200px; 18 | height: auto; 19 | top: 20px; 20 | right: 10px; 21 | // margin-left: -(200px / 2); 22 | display: flex; 23 | justify-content: space-around; 24 | align-items: center; 25 | a.linkIcon{ 26 | display: block; 27 | width: 50px; 28 | height: 50px; 29 | background-size: 100% 100%; 30 | animation: loading_coin_breath 1s infinite linear; 31 | color: #fff; 32 | } 33 | } 34 | 35 | .ui{ 36 | display: block; 37 | position: absolute; 38 | &.footer{ 39 | width: 100%; 40 | height: 107px; 41 | bottom: 0; 42 | left: 0; 43 | background-image: url("../images/footer.png"); 44 | background-position: center; 45 | } 46 | &.tree1{ 47 | width: 155px; 48 | height: 288px; 49 | background-image: url("https://ipfs.infura.io/ipfs/Qmck3FzLLHkypSerue1BBzQkawhN2YhWQSnwFzgdd5E8Jo"); 50 | left: -50px; 51 | bottom: 0; 52 | } 53 | &.tree2{ 54 | width: 300px; 55 | height: 577px; 56 | background-image: url("https://ipfs.infura.io/ipfs/QmUPEBPCySpCjqsCvmePvgmh4gvQZG5bYMnRjShnYMwb72"); 57 | right: -65px; 58 | bottom: 0; 59 | } 60 | &.cloud{ 61 | top: 20%; 62 | left: 10%; 63 | width: 68px; 64 | height: 27px; 65 | background-image: url("../images/cloud.png"); 66 | animation: cloudMove1 18s infinite linear; 67 | } 68 | &.cloud2{ 69 | top: 50%; 70 | right: 13%; 71 | width: 68px; 72 | height: 27px; 73 | background-image: url("../images/cloud.png"); 74 | animation: cloudMove2 10s infinite linear; 75 | } 76 | &.start1{ 77 | width: 13px; 78 | height: 20px; 79 | background-image: url("../images/star1.png"); 80 | top: 30%; 81 | left: 20%; 82 | animation: starMove1 3s infinite linear; 83 | } 84 | &.start2{ 85 | width: 13px; 86 | height: 21px; 87 | background-image: url("../images/star2.png"); 88 | top: 37%; 89 | right: 26%; 90 | animation: starMove2 2s infinite linear; 91 | } 92 | &.start3{ 93 | width: 13px; 94 | height: 20px; 95 | background-image: url("../images/star1.png"); 96 | top: 75%; 97 | left: 30%; 98 | animation: starMove3 4s infinite linear; 99 | } 100 | &.Elf0{ 101 | pointer-events: none; 102 | top: 0; 103 | left: 0; 104 | width: 38px; 105 | height: 41px; 106 | background-image: url("../images/Elf0.png"); 107 | } 108 | &.Elf1{ 109 | bottom: 39%; 110 | left: 120px; 111 | width: 26px; 112 | height: 25px; 113 | background-image: url("../images/Elf1.png"); 114 | animation: ElfMove 2s infinite linear; 115 | } 116 | &.Elf2{ 117 | top: 15%; 118 | left: 25%; 119 | width: 26px; 120 | height: 25px; 121 | background-image: url("../images/Elf2.png"); 122 | animation: ElfMove 2s infinite linear; 123 | } 124 | &.Elf3{ 125 | bottom: 15%; 126 | right: 25%; 127 | width: 26px; 128 | height: 25px; 129 | background-image: url("../images/Elf3.png"); 130 | animation: ElfMove 2s infinite linear; 131 | } 132 | &.fruit1{ 133 | bottom: 10%; 134 | left: 20%; 135 | width: 26px; 136 | height: 31px; 137 | background-image: url("../images/fruit1.png"); 138 | animation: ElfMove 2s infinite linear; 139 | } 140 | &.fruit2{ 141 | top: 25%; 142 | right: 7%; 143 | width: 22px; 144 | height: 33px; 145 | background-image: url("../images/fruit2.png"); 146 | animation: ElfMove 2s infinite linear; 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /dapp/src/constants/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const ADD = 'ADD'; 2 | export const SUB = 'SUB'; 3 | 4 | export const HEALTH = 'HEALTH'; 5 | export const HEALTH_STATUS = 'HEALTH_STATUS'; 6 | 7 | export const SYSTEM_ERROR = 'SYSTEM_ERROR'; 8 | 9 | // for MetaMask 10 | export const METAMASK_ACCOUNT = 'METAMASK_ACCOUNT'; 11 | export const METAMASK_NETWORK = 'METAMASK_NETWORK'; 12 | 13 | // for SimpleToken contract 14 | export const SIMPLE_TOKEN_NAME = 'SIMPLE_TOKEN_NAME'; 15 | export const SIMPLE_TOKEN_NAME_SUCCESS = 'SIMPLE_TOKEN_NAME_SUCCESS'; 16 | export const SIMPLE_TOKEN_SYMBOL = 'SIMPLE_TOKEN_SYMBOL'; 17 | export const SIMPLE_TOKEN_SYMBOL_SUCCESS = 'SIMPLE_TOKEN_SYMBOL_SUCCESS'; 18 | export const SIMPLE_TOKEN_DECIMALS = 'SIMPLE_TOKEN_DECIMALS'; 19 | export const SIMPLE_TOKEN_DECIMALS_SUCCESS = 'SIMPLE_TOKEN_DECIMALS_SUCCESS'; 20 | 21 | // for showing progress/onloading 22 | export const FETCHING = 'FETCHING'; 23 | export const FETCH_COMPLETE = 'FETCH_COMPLETE'; 24 | 25 | // for showing warning message 26 | export const WARNING_OPEN = 'WARNING_OPEN'; 27 | export const WARNING_CLOSE = 'WARNING_CLOSE'; 28 | 29 | // for LOLYAT contract 30 | export const LOLYAT_NAME = 'LOLYAT_NAME'; 31 | export const LOLYAT_NAME_SUCCESS = 'LOLYAT_NAME_SUCCESS'; 32 | export const LOLYAT_SYMBOL = 'LOLYAT_SYMBOL'; 33 | export const LOLYAT_SYMBOL_SUCCESS = 'LOLYAT_SYMBOL_SUCCESS'; 34 | export const LOLYAT_TOKEN_URI = 'LOLYAT_TOKEN_URI'; 35 | export const LOLYAT_TOKEN_URI_SUCCESS = 'LOLYA_TOKEN_URI_SUCCESS'; 36 | export const LOLYAT_OWNER_OF = 'LOLYAT_OWNER_OF'; 37 | export const LOLYAT_OWNER_OF_SUCCESS = 'LOLYAT_OWNER_OF_SUCCESS'; 38 | export const LOLYAT_TRANSFER_OWNERSHIP = 'LOLYAT_TRANSFER_OWNERSHIP'; 39 | export const LOLYAT_TRANSFER_OWNERSHIP_SUCCESS = 'LOLYAT_TRANSFER_OWNERSHIP_SUCCESS'; 40 | export const LOLYAT_GET_OWNED_TOKENS = 'LOLYAT_GET_OWNED_TOKENS'; 41 | export const LOLYAT_GET_OWNED_TOKENS_SUCCESS = 'LOLYAT_GET_OWNED_TOKENS_SUCCESS'; 42 | -------------------------------------------------------------------------------- /dapp/src/container/AppContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import App from '../components/App'; 3 | import { 4 | addAction, 5 | subAction 6 | } from '../actions/counterActions'; 7 | import { healthAction } from '../actions/healthActions'; 8 | import { 9 | simpleTokenNameAction, 10 | simpleTokenSymbolAction, 11 | simpleTokenDecimalsAction, 12 | } from '../actions/simpleTokenActions'; 13 | import { 14 | warningOpenAction, 15 | warningCloseAction 16 | } from '../actions/warningActions'; 17 | import { 18 | metaMaskAccountAction, 19 | metaMaskNetworkAction 20 | } from '../actions/metaMaskActions'; 21 | import { 22 | LOLYATNameAction, 23 | LOLYATSymbolAction, 24 | LOLYATGetOwnedTokensAction, 25 | LOLYATTokenURIAction 26 | } from '../actions/LOLYATActions'; 27 | 28 | const mapStateToProps = (state) => ({ 29 | isFetching: state.isFetching, 30 | count: state.count, 31 | error: state.error, 32 | health: state.health, 33 | simpleToken: state.simpleToken, 34 | warning: state.warning, 35 | metaMask: state.metaMask, 36 | LOLYATToken: state.LOLYATToken, 37 | LOLYATOwned: state.LOLYATOwned, 38 | LOLYATOwnedTokenURI: state.LOLYATOwnedTokenURI 39 | }); 40 | 41 | const mapDispatchToProps = (dispatch) => { 42 | return { 43 | handleAdd: (num) => { 44 | dispatch(addAction(num)); 45 | }, 46 | handleSub: (num) => { 47 | dispatch(subAction(num)); 48 | }, 49 | handleHealth: () => { 50 | dispatch(healthAction()); 51 | }, 52 | handleSimpleTokenName: (networkId) => { 53 | dispatch(simpleTokenNameAction(networkId)); 54 | }, 55 | handleSimpleTokenSymbol: (networkId) => { 56 | dispatch(simpleTokenSymbolAction(networkId)); 57 | }, 58 | handleSimpleTokenDecimals: (networkId) => { 59 | dispatch(simpleTokenDecimalsAction(networkId)); 60 | }, 61 | handleLOLYATName: (networkId) => { 62 | dispatch(LOLYATNameAction(networkId)); 63 | }, 64 | handleLOLYATSymbol: (networkId) => { 65 | dispatch(LOLYATSymbolAction(networkId)); 66 | }, 67 | handleLOLYATGetOwnedTokens: (networkId, address, callBack) => { 68 | dispatch(LOLYATGetOwnedTokensAction(networkId, address, callBack)); 69 | }, 70 | handleLOLYATTokenURI: (networkId, tokenId, callBack) => { 71 | dispatch(LOLYATTokenURIAction(networkId, tokenId, callBack)); 72 | }, 73 | handleWarningOpen: (message) => { 74 | dispatch(warningOpenAction(message)); 75 | }, 76 | handleWarningClose: () => { 77 | dispatch(warningCloseAction()); 78 | }, 79 | handleMetaMaskAccount: (account) => { 80 | dispatch(metaMaskAccountAction(account)); 81 | }, 82 | handleMetaMaskNetwork: (network) => { 83 | dispatch(metaMaskNetworkAction(network)); 84 | } 85 | } 86 | }; 87 | 88 | export default connect( 89 | mapStateToProps, 90 | mapDispatchToProps 91 | )(App); -------------------------------------------------------------------------------- /dapp/src/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/.DS_Store -------------------------------------------------------------------------------- /dapp/src/images/Elf0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/Elf0.png -------------------------------------------------------------------------------- /dapp/src/images/Elf1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/Elf1.png -------------------------------------------------------------------------------- /dapp/src/images/Elf2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/Elf2.png -------------------------------------------------------------------------------- /dapp/src/images/Elf3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/Elf3.png -------------------------------------------------------------------------------- /dapp/src/images/Tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/Tutorial.png -------------------------------------------------------------------------------- /dapp/src/images/addcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/addcard.png -------------------------------------------------------------------------------- /dapp/src/images/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/back.png -------------------------------------------------------------------------------- /dapp/src/images/bgfooter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/bgfooter.png -------------------------------------------------------------------------------- /dapp/src/images/big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/big.png -------------------------------------------------------------------------------- /dapp/src/images/cardtitle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/cardtitle.png -------------------------------------------------------------------------------- /dapp/src/images/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/cloud.png -------------------------------------------------------------------------------- /dapp/src/images/contarctresult.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/contarctresult.png -------------------------------------------------------------------------------- /dapp/src/images/demo/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/demo/15.png -------------------------------------------------------------------------------- /dapp/src/images/demo/Pixel15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/demo/Pixel15.png -------------------------------------------------------------------------------- /dapp/src/images/demo/bg15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/demo/bg15.png -------------------------------------------------------------------------------- /dapp/src/images/draw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/draw.png -------------------------------------------------------------------------------- /dapp/src/images/draw_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/draw_message.png -------------------------------------------------------------------------------- /dapp/src/images/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/footer.png -------------------------------------------------------------------------------- /dapp/src/images/fruit1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/fruit1.png -------------------------------------------------------------------------------- /dapp/src/images/fruit2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/fruit2.png -------------------------------------------------------------------------------- /dapp/src/images/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/game.png -------------------------------------------------------------------------------- /dapp/src/images/gameplaybtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/gameplaybtn.png -------------------------------------------------------------------------------- /dapp/src/images/gameplaytitle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/gameplaytitle.png -------------------------------------------------------------------------------- /dapp/src/images/getcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/getcard.png -------------------------------------------------------------------------------- /dapp/src/images/getherobtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/getherobtn.png -------------------------------------------------------------------------------- /dapp/src/images/github-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/github-icon.png -------------------------------------------------------------------------------- /dapp/src/images/github-icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/github-icon2.png -------------------------------------------------------------------------------- /dapp/src/images/goBack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/goBack.png -------------------------------------------------------------------------------- /dapp/src/images/history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/history.png -------------------------------------------------------------------------------- /dapp/src/images/inputETH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/inputETH.png -------------------------------------------------------------------------------- /dapp/src/images/ladder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/ladder.png -------------------------------------------------------------------------------- /dapp/src/images/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/loading.png -------------------------------------------------------------------------------- /dapp/src/images/loadingicoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/loadingicoin.png -------------------------------------------------------------------------------- /dapp/src/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/logo.png -------------------------------------------------------------------------------- /dapp/src/images/lostelf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/lostelf.png -------------------------------------------------------------------------------- /dapp/src/images/loveicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/loveicon.png -------------------------------------------------------------------------------- /dapp/src/images/paayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/paayer.png -------------------------------------------------------------------------------- /dapp/src/images/playerbet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/playerbet.png -------------------------------------------------------------------------------- /dapp/src/images/playgame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/playgame.png -------------------------------------------------------------------------------- /dapp/src/images/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/result.png -------------------------------------------------------------------------------- /dapp/src/images/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/select.png -------------------------------------------------------------------------------- /dapp/src/images/star1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/star1.png -------------------------------------------------------------------------------- /dapp/src/images/star2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/star2.png -------------------------------------------------------------------------------- /dapp/src/images/testCard1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/testCard1.png -------------------------------------------------------------------------------- /dapp/src/images/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/title.png -------------------------------------------------------------------------------- /dapp/src/images/title2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/title2.png -------------------------------------------------------------------------------- /dapp/src/images/titlelogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/titlelogo.png -------------------------------------------------------------------------------- /dapp/src/images/tree1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/tree1.png -------------------------------------------------------------------------------- /dapp/src/images/tree2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/tree2.png -------------------------------------------------------------------------------- /dapp/src/images/user/user1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user1.png -------------------------------------------------------------------------------- /dapp/src/images/user/user10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user10.png -------------------------------------------------------------------------------- /dapp/src/images/user/user11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user11.png -------------------------------------------------------------------------------- /dapp/src/images/user/user12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user12.png -------------------------------------------------------------------------------- /dapp/src/images/user/user13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user13.png -------------------------------------------------------------------------------- /dapp/src/images/user/user14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user14.png -------------------------------------------------------------------------------- /dapp/src/images/user/user2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user2.png -------------------------------------------------------------------------------- /dapp/src/images/user/user3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user3.png -------------------------------------------------------------------------------- /dapp/src/images/user/user4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user4.png -------------------------------------------------------------------------------- /dapp/src/images/user/user5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user5.png -------------------------------------------------------------------------------- /dapp/src/images/user/user6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user6.png -------------------------------------------------------------------------------- /dapp/src/images/user/user7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user7.png -------------------------------------------------------------------------------- /dapp/src/images/user/user8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user8.png -------------------------------------------------------------------------------- /dapp/src/images/user/user9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/user/user9.png -------------------------------------------------------------------------------- /dapp/src/images/userresult.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/userresult.png -------------------------------------------------------------------------------- /dapp/src/images/winlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/winlogo.png -------------------------------------------------------------------------------- /dapp/src/images/youlost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/dapp/src/images/youlost.png -------------------------------------------------------------------------------- /dapp/src/index.css: -------------------------------------------------------------------------------- 1 | *{ 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | html, body { 7 | font-family: sans-serif; 8 | background-color: #272424; 9 | width: 100%; 10 | height: 100%; 11 | } 12 | 13 | img{ 14 | display: block; 15 | width: 100%; 16 | height: 100%; 17 | } 18 | a{ 19 | cursor: pointer; 20 | } 21 | #root{ 22 | width: 100%; 23 | height: 100%; 24 | } -------------------------------------------------------------------------------- /dapp/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import './index.css'; 5 | import AppContainer from './container/AppContainer'; 6 | import configureStore from './store/configureStore'; 7 | import registerServiceWorker from './registerServiceWorker'; 8 | 9 | const store = configureStore(); 10 | 11 | ReactDOM.render(<Provider store={store}><AppContainer /></Provider>, 12 | document.getElementById('root')); 13 | registerServiceWorker(); 14 | -------------------------------------------------------------------------------- /dapp/src/lib/LOLYAT.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minTokensBeforeSwap","type":"uint256"}],"name":"MinTokensBeforeSwapUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokensSwapped","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensIntoLiqudity","type":"uint256"}],"name":"SwapAndLiquify","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"SwapAndLiquifyEnabledUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"_liquidityFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_maxTxAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_taxFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tAmount","type":"uint256"}],"name":"deliver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"excludeFromFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"excludeFromReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"geUnlockTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"includeInFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"includeInReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isExcludedFromFee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isExcludedFromReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"time","type":"uint256"}],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tAmount","type":"uint256"},{"internalType":"bool","name":"deductTransferFee","type":"bool"}],"name":"reflectionFromToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidityFee","type":"uint256"}],"name":"setLiquidityFeePercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxTxPercent","type":"uint256"}],"name":"setMaxTxPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setSwapAndLiquifyEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"taxFee","type":"uint256"}],"name":"setTaxFeePercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapAndLiquifyEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rAmount","type":"uint256"}],"name":"tokenFromReflection","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapV2Pair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapV2Router","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] -------------------------------------------------------------------------------- /dapp/src/lib/LOLYATService.js: -------------------------------------------------------------------------------- 1 | import { getProvider, getLOLYATAddress } from './web3Service'; 2 | import LOLYAT from './LOLYAT'; 3 | 4 | const Web3 = require('web3'); 5 | 6 | let web3 = new Web3(); 7 | let LOLYATAddress = '0xabb4b0608749E72A3016E424b87AF56846f939dC'; 8 | let LOLYAT = null; 9 | 10 | const setWeb3Provider = (networkId) => { 11 | web3.setProvider(new web3.providers.HttpProvider(getProvider(networkId))); 12 | LOLYATAddress = getLOLYATAddress(networkId); 13 | LOLYAT = new LOLYAT(web3, LOLYATAddress); 14 | } 15 | 16 | export const getName = (networkId) => { 17 | try { 18 | setWeb3Provider(networkId); 19 | const result = LOLYAT.name(); 20 | return result; 21 | } catch (err) { 22 | console.log('getName: ', err); 23 | return 'name not found'; 24 | } 25 | } 26 | 27 | export const getSymbol = (networkId) => { 28 | try { 29 | setWeb3Provider(networkId); 30 | const result = LOLYAT.symbol(); 31 | return result; 32 | } catch (err) { 33 | console.log('getSymbol: ', err); 34 | return 'symbol not found'; 35 | } 36 | } 37 | 38 | export const getOwnerOf = (networkId, tokenId) => { 39 | try { 40 | setWeb3Provider(networkId); 41 | const result = LOLYAT.ownerOf(tokenId); 42 | return result; 43 | } catch (err) { 44 | console.log('getOwnerOf: ', err); 45 | return 'ownerOf not found'; 46 | } 47 | } 48 | 49 | //卡片細節 50 | export const doGetTokenProperty = async (networkId, tokenId) => { 51 | try { 52 | setWeb3Provider(networkId); 53 | const result = await LOLYAT.getTokenProperty(tokenId); 54 | return result; 55 | } catch (err) { 56 | console.log('doGetTokenProperty: ', err); 57 | return 'getTokenProperty not found'; 58 | } 59 | } 60 | 61 | export const doTransferOwnership = (networkId, address) => { 62 | try { 63 | setWeb3Provider(networkId); 64 | const result = LOLYAT.transferOwnership(address); 65 | return result; 66 | } catch (err) { 67 | console.log('doTransferOwnership: ', err); 68 | return 'transferOwnership not found'; 69 | } 70 | } 71 | 72 | //我有多少卡片編號 73 | export const doGetOwnedTokens = (networkId, address) => { 74 | try { 75 | setWeb3Provider(networkId); 76 | const result = LOLYAT.getOwnedTokens(address); 77 | return result; 78 | } catch (err) { 79 | console.log('doGetOwnedTokens: ', err); 80 | return 'getOwnedTokens not found'; 81 | } 82 | } 83 | 84 | export const doGetHerosLength = (networkId) => { 85 | try { 86 | setWeb3Provider(networkId); 87 | const result = LOLYAT.getHerosLength(); 88 | return result; 89 | } catch (err) { 90 | console.log('doGetHerosLength: ', err); 91 | return 'getHerosLength not found'; 92 | } 93 | } 94 | 95 | //創造卡片 96 | export const doMint = () => { 97 | return LOLYAT.mint(); 98 | } 99 | -------------------------------------------------------------------------------- /dapp/src/lib/LOLYATService.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | getName, 3 | getSymbol, 4 | } from './cryptoHerosTokenService'; 5 | 6 | test('getName', async () => { 7 | try { 8 | const name = await getName('3'); 9 | console.log('name', name); 10 | } catch (err) { 11 | console.log(err); 12 | } 13 | }); 14 | 15 | test('getSymbol', async () => { 16 | try { 17 | const symbol = await getSymbol('3'); 18 | console.log('symbol', symbol); 19 | } catch (err) { 20 | console.log(err); 21 | } 22 | }); -------------------------------------------------------------------------------- /dapp/src/lib/LowBit.js: -------------------------------------------------------------------------------- 1 | var Promise = require('bluebird'); 2 | const abi = require('ethereumjs-abi'); 3 | 4 | const LowBitInterface = [ 5 | { 6 | "anonymous": false, 7 | "inputs": [ 8 | { 9 | "indexed": true, 10 | "name": "previousOwner", 11 | "type": "address" 12 | }, 13 | { 14 | "indexed": true, 15 | "name": "newOwner", 16 | "type": "address" 17 | } 18 | ], 19 | "name": "OwnershipTransferred", 20 | "type": "event" 21 | }, 22 | { 23 | "anonymous": false, 24 | "inputs": [ 25 | { 26 | "indexed": true, 27 | "name": "previousOwner", 28 | "type": "address" 29 | } 30 | ], 31 | "name": "OwnershipRenounced", 32 | "type": "event" 33 | }, 34 | { 35 | "constant": false, 36 | "inputs": [ 37 | { 38 | "name": "_tokenId", 39 | "type": "uint256" 40 | } 41 | ], 42 | "name": "createSingleGame", 43 | "outputs": [ 44 | { 45 | "name": "", 46 | "type": "uint256" 47 | } 48 | ], 49 | "payable": true, 50 | "stateMutability": "payable", 51 | "type": "function" 52 | }, 53 | { 54 | "constant": false, 55 | "inputs": [], 56 | "name": "renounceOwnership", 57 | "outputs": [], 58 | "payable": false, 59 | "stateMutability": "nonpayable", 60 | "type": "function" 61 | }, 62 | { 63 | "constant": false, 64 | "inputs": [ 65 | { 66 | "name": "_newOwner", 67 | "type": "address" 68 | } 69 | ], 70 | "name": "transferOwnership", 71 | "outputs": [], 72 | "payable": false, 73 | "stateMutability": "nonpayable", 74 | "type": "function" 75 | }, 76 | { 77 | "constant": false, 78 | "inputs": [ 79 | { 80 | "name": "amount", 81 | "type": "uint256" 82 | } 83 | ], 84 | "name": "withdraw", 85 | "outputs": [ 86 | { 87 | "name": "", 88 | "type": "bool" 89 | } 90 | ], 91 | "payable": true, 92 | "stateMutability": "payable", 93 | "type": "function" 94 | }, 95 | { 96 | "inputs": [ 97 | { 98 | "name": "_cryptoHerosToken", 99 | "type": "address" 100 | } 101 | ], 102 | "payable": false, 103 | "stateMutability": "nonpayable", 104 | "type": "constructor" 105 | }, 106 | { 107 | "payable": true, 108 | "stateMutability": "payable", 109 | "type": "fallback" 110 | }, 111 | { 112 | "constant": true, 113 | "inputs": [ 114 | { 115 | "name": "_address", 116 | "type": "address" 117 | } 118 | ], 119 | "name": "getUserSingleGames", 120 | "outputs": [ 121 | { 122 | "name": "", 123 | "type": "uint256[]" 124 | } 125 | ], 126 | "payable": false, 127 | "stateMutability": "view", 128 | "type": "function" 129 | }, 130 | { 131 | "constant": true, 132 | "inputs": [], 133 | "name": "maxSingleGameId", 134 | "outputs": [ 135 | { 136 | "name": "", 137 | "type": "uint256" 138 | } 139 | ], 140 | "payable": false, 141 | "stateMutability": "view", 142 | "type": "function" 143 | }, 144 | { 145 | "constant": true, 146 | "inputs": [], 147 | "name": "owner", 148 | "outputs": [ 149 | { 150 | "name": "", 151 | "type": "address" 152 | } 153 | ], 154 | "payable": false, 155 | "stateMutability": "view", 156 | "type": "function" 157 | }, 158 | { 159 | "constant": true, 160 | "inputs": [ 161 | { 162 | "name": "", 163 | "type": "uint256" 164 | } 165 | ], 166 | "name": "singleGames", 167 | "outputs": [ 168 | { 169 | "name": "player", 170 | "type": "address" 171 | }, 172 | { 173 | "name": "userResult", 174 | "type": "uint256" 175 | }, 176 | { 177 | "name": "contractResult", 178 | "type": "uint256" 179 | }, 180 | { 181 | "name": "playerBet", 182 | "type": "uint256" 183 | }, 184 | { 185 | "name": "game", 186 | "type": "uint8" 187 | }, 188 | { 189 | "name": "result", 190 | "type": "uint8" 191 | } 192 | ], 193 | "payable": false, 194 | "stateMutability": "view", 195 | "type": "function" 196 | }, 197 | { 198 | "constant": true, 199 | "inputs": [ 200 | { 201 | "name": "", 202 | "type": "address" 203 | }, 204 | { 205 | "name": "", 206 | "type": "uint256" 207 | } 208 | ], 209 | "name": "usersSingleGames", 210 | "outputs": [ 211 | { 212 | "name": "", 213 | "type": "uint256" 214 | } 215 | ], 216 | "payable": false, 217 | "stateMutability": "view", 218 | "type": "function" 219 | } 220 | ]; 221 | 222 | function LowBit(web3, address) { 223 | this.web3 = web3; 224 | const LowBitContract = web3.eth.contract(LowBitInterface); 225 | this.LowBitPromise = Promise.resolve(Promise.promisifyAll(LowBitContract.at(address))); 226 | } 227 | 228 | LowBit.prototype.createSingleGame = function (tokenId, callback) { 229 | let byteData = "0x" + 230 | abi.methodID("createSingleGame", ["uint"]).toString("hex") + 231 | abi.rawEncode(["uint"], 232 | [tokenId]).toString("hex"); 233 | return byteData; 234 | } 235 | 236 | LowBit.prototype.getUserSingleGames = function (address, callback) { 237 | return this.LowBitPromise.then(function (LowBit) { 238 | return LowBit.getUserSingleGamesAsync(address); 239 | }); 240 | } 241 | 242 | LowBit.prototype.singleGames = function (gameId, callback) { 243 | return this.LowBitPromise.then(function (LowBit) { 244 | return LowBit.singleGamesAsync(gameId); 245 | }) 246 | } 247 | 248 | module.exports = LowBit; -------------------------------------------------------------------------------- /dapp/src/lib/LowBitService.js: -------------------------------------------------------------------------------- 1 | import { getProvider, getLowBitAddress } from './web3Service'; 2 | import LowBit from './LowBit'; 3 | 4 | const Web3 = require('web3'); 5 | 6 | let web3 = new Web3(); 7 | let LowBitAddress = '0x0'; 8 | let LowBit = null; 9 | 10 | const setWeb3Provider = (networkId) => { 11 | web3.setProvider(new web3.providers.HttpProvider(getProvider(networkId))); 12 | LowBitAddress = getLowBitAddress(networkId); 13 | LowBit = new LowBit(web3, LowBitAddress); 14 | } 15 | 16 | export const doCreateSingleGame = (networkId, tokenId) => { 17 | setWeb3Provider(networkId); 18 | return LowBit.createSingleGame(tokenId); 19 | } 20 | 21 | export const doGetUserSingleGames = (networkId, addres) => { 22 | try { 23 | setWeb3Provider(networkId); 24 | const result = LowBit.getUserSingleGames(addres); 25 | return result; 26 | } catch (err) { 27 | console.log('doGetUserSingleGames: ', err); 28 | return 'getUserSingleGames not found'; 29 | } 30 | } 31 | 32 | export const getSingleGame = (networkId, gameId, address) => { 33 | try { 34 | setWeb3Provider(networkId); 35 | const result = LowBit.singleGames(gameId); 36 | return result; 37 | } catch (err) { 38 | console.log('getSingleGame: ', err); 39 | return 'singleGame not found'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dapp/src/lib/cryptoHerosGame.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "name": "previousOwner", 8 | "type": "address" 9 | }, 10 | { 11 | "indexed": true, 12 | "name": "newOwner", 13 | "type": "address" 14 | } 15 | ], 16 | "name": "OwnershipTransferred", 17 | "type": "event" 18 | }, 19 | { 20 | "anonymous": false, 21 | "inputs": [ 22 | { 23 | "indexed": true, 24 | "name": "previousOwner", 25 | "type": "address" 26 | } 27 | ], 28 | "name": "OwnershipRenounced", 29 | "type": "event" 30 | }, 31 | { 32 | "constant": false, 33 | "inputs": [ 34 | { 35 | "name": "_tokenId", 36 | "type": "uint256" 37 | } 38 | ], 39 | "name": "createSingleGame", 40 | "outputs": [ 41 | { 42 | "name": "", 43 | "type": "uint256" 44 | } 45 | ], 46 | "payable": true, 47 | "stateMutability": "payable", 48 | "type": "function" 49 | }, 50 | { 51 | "constant": false, 52 | "inputs": [], 53 | "name": "renounceOwnership", 54 | "outputs": [], 55 | "payable": false, 56 | "stateMutability": "nonpayable", 57 | "type": "function" 58 | }, 59 | { 60 | "constant": false, 61 | "inputs": [ 62 | { 63 | "name": "_newOwner", 64 | "type": "address" 65 | } 66 | ], 67 | "name": "transferOwnership", 68 | "outputs": [], 69 | "payable": false, 70 | "stateMutability": "nonpayable", 71 | "type": "function" 72 | }, 73 | { 74 | "constant": false, 75 | "inputs": [ 76 | { 77 | "name": "amount", 78 | "type": "uint256" 79 | } 80 | ], 81 | "name": "withdraw", 82 | "outputs": [ 83 | { 84 | "name": "", 85 | "type": "bool" 86 | } 87 | ], 88 | "payable": true, 89 | "stateMutability": "payable", 90 | "type": "function" 91 | }, 92 | { 93 | "inputs": [ 94 | { 95 | "name": "_cryptoHerosToken", 96 | "type": "address" 97 | } 98 | ], 99 | "payable": false, 100 | "stateMutability": "nonpayable", 101 | "type": "constructor" 102 | }, 103 | { 104 | "payable": true, 105 | "stateMutability": "payable", 106 | "type": "fallback" 107 | }, 108 | { 109 | "constant": true, 110 | "inputs": [ 111 | { 112 | "name": "_address", 113 | "type": "address" 114 | } 115 | ], 116 | "name": "getUserSingleGames", 117 | "outputs": [ 118 | { 119 | "name": "", 120 | "type": "uint256[]" 121 | } 122 | ], 123 | "payable": false, 124 | "stateMutability": "view", 125 | "type": "function" 126 | }, 127 | { 128 | "constant": true, 129 | "inputs": [], 130 | "name": "maxSingleGameId", 131 | "outputs": [ 132 | { 133 | "name": "", 134 | "type": "uint256" 135 | } 136 | ], 137 | "payable": false, 138 | "stateMutability": "view", 139 | "type": "function" 140 | }, 141 | { 142 | "constant": true, 143 | "inputs": [], 144 | "name": "owner", 145 | "outputs": [ 146 | { 147 | "name": "", 148 | "type": "address" 149 | } 150 | ], 151 | "payable": false, 152 | "stateMutability": "view", 153 | "type": "function" 154 | }, 155 | { 156 | "constant": true, 157 | "inputs": [ 158 | { 159 | "name": "", 160 | "type": "uint256" 161 | } 162 | ], 163 | "name": "singleGames", 164 | "outputs": [ 165 | { 166 | "name": "player", 167 | "type": "address" 168 | }, 169 | { 170 | "name": "userResult", 171 | "type": "uint256" 172 | }, 173 | { 174 | "name": "contractResult", 175 | "type": "uint256" 176 | }, 177 | { 178 | "name": "playerBet", 179 | "type": "uint256" 180 | }, 181 | { 182 | "name": "game", 183 | "type": "uint8" 184 | }, 185 | { 186 | "name": "result", 187 | "type": "uint8" 188 | } 189 | ], 190 | "payable": false, 191 | "stateMutability": "view", 192 | "type": "function" 193 | }, 194 | { 195 | "constant": true, 196 | "inputs": [ 197 | { 198 | "name": "", 199 | "type": "address" 200 | }, 201 | { 202 | "name": "", 203 | "type": "uint256" 204 | } 205 | ], 206 | "name": "usersSingleGames", 207 | "outputs": [ 208 | { 209 | "name": "", 210 | "type": "uint256" 211 | } 212 | ], 213 | "payable": false, 214 | "stateMutability": "view", 215 | "type": "function" 216 | } 217 | ] -------------------------------------------------------------------------------- /dapp/src/lib/tokenService.js: -------------------------------------------------------------------------------- 1 | import { getProvider, getSimpleTokenAddress } from './web3Service'; 2 | import SimpleToken from './simpleToken'; 3 | 4 | const Web3 = require('web3'); 5 | 6 | let web3 = new Web3(); 7 | let simpleTokenAddress = '0x0'; 8 | 9 | const setWeb3Provider = (networkId) => { 10 | web3.setProvider(new web3.providers.HttpProvider(getProvider(networkId))); 11 | simpleTokenAddress = getSimpleTokenAddress(networkId); 12 | } 13 | 14 | export const getName = (networkId) => { 15 | try { 16 | setWeb3Provider(networkId); 17 | const simpleToken = new SimpleToken(web3, simpleTokenAddress); 18 | const result = simpleToken.name(); 19 | return result; 20 | } catch (err) { 21 | console.log('getName: ', err); 22 | return 'name not found'; 23 | } 24 | } 25 | 26 | export const getSymbol = (networkId) => { 27 | try { 28 | setWeb3Provider(networkId); 29 | const simpleToken = new SimpleToken(web3, simpleTokenAddress); 30 | const result = simpleToken.symbol(); 31 | return result; 32 | } catch (err) { 33 | console.log('getSymbol: ', err); 34 | return 'symbol not found'; 35 | } 36 | } 37 | 38 | export const getDecimals = (networkId) => { 39 | try { 40 | setWeb3Provider(networkId); 41 | const simpleToken = new SimpleToken(web3, simpleTokenAddress); 42 | const result = simpleToken.decimals(); 43 | return result; 44 | } catch (err) { 45 | console.log('getDecimals: ', err); 46 | return 'decimals not found'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /dapp/src/lib/tokenService.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | getName, 3 | getSymbol, 4 | getDecimals 5 | } from './tokenService'; 6 | 7 | test('getName', async () => { 8 | try { 9 | const name = await getName('3'); 10 | console.log('name', name); 11 | } catch(err) { 12 | console.log(err); 13 | } 14 | }); 15 | 16 | test('getSymbol', async () => { 17 | try { 18 | const symbol = await getSymbol('3'); 19 | console.log('symbol', symbol); 20 | } catch(err) { 21 | console.log(err); 22 | } 23 | }); 24 | 25 | test('getDecimals', async () => { 26 | try { 27 | const decimals = await getDecimals('3'); 28 | console.log('decimals', decimals); 29 | } catch(err) { 30 | console.log(err); 31 | } 32 | }); -------------------------------------------------------------------------------- /dapp/src/lib/web3Service.js: -------------------------------------------------------------------------------- 1 | export const getProvider = (networkId) => { 2 | switch (networkId) { 3 | case '1': 4 | return 'https://mainnet.infura.io/'; 5 | case '3': 6 | return 'https://ropsten.infura.io/'; 7 | case '4': 8 | return 'https://rinkeby.infura.io/'; 9 | case '42': 10 | return 'https://kovan.infura.io/'; 11 | default: 12 | return 'http://localhost:8545/'; 13 | } 14 | } 15 | 16 | export const getSimpleTokenAddress = (networkId) => { 17 | switch (networkId) { 18 | case '1': 19 | return '0x0'; 20 | case '3': 21 | return '0x131855dda0aaff096f6854854c55a4debf61077a'; 22 | case '4': 23 | return '0x0'; 24 | case '42': 25 | return '0x0'; 26 | default: 27 | return '0x0'; 28 | } 29 | } 30 | 31 | export const getCryptoHerosTokenAddress = (networkId) => { 32 | switch (networkId) { 33 | case '1': 34 | return '0x0'; 35 | case '3': 36 | return '0xa82Bc392bF65d03A796E1666d27594fB31De4B93'; 37 | case '4': 38 | return '0x0'; 39 | case '42': 40 | return '0x0'; 41 | default: 42 | return '0x0'; 43 | } 44 | } 45 | 46 | export const getCryptoHerosGameAddress = (networkId) => { 47 | switch (networkId) { 48 | case '1': 49 | return '0x0'; 50 | case '3': 51 | return '0xb4FF27d8cD1C5b1e3D4BD8A8FFEBdA9BE9517a4b'; 52 | case '4': 53 | return '0x0'; 54 | case '42': 55 | return '0x0'; 56 | default: 57 | return '0x0'; 58 | } 59 | } 60 | 61 | export const getCurrentAddress = (web3) => { 62 | if (web3 === null) return; 63 | return web3.eth.accounts[0]; 64 | } 65 | 66 | export const getCurrentNetwork = (web3) => { 67 | if (web3 === null) return; 68 | return web3.version.network; 69 | } 70 | -------------------------------------------------------------------------------- /dapp/src/reducers/LOLYATOwnedReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | const fetchOwned = (state, action) => { 5 | // console.log("fetchOwned:", action.result); 6 | if(action.callBack) action.callBack(action.result); 7 | return action.result; 8 | } 9 | 10 | export default function (LOLYATOwned = initialState.LOLYATOwned, action) { 11 | switch (action.type) { 12 | case types.LOLYAT_GET_OWNED_TOKENS_SUCCESS: 13 | return fetchOwned(LOLYATOwned, action); 14 | default: 15 | return LOLYATOwned; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /dapp/src/reducers/LOLYATOwnedTokenURIReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | const fetchOwnedTokenURI = (state, action) => { 5 | let tokenURI = JSON.parse(action.result); 6 | let data = { 7 | name: tokenURI.name, 8 | image: 'https://ipfs.infura.io/ipfs/' + tokenURI.image, 9 | HP: tokenURI.HP, 10 | ATK: tokenURI.ATK, 11 | DEF: tokenURI.DEF 12 | }; 13 | if(action.callBack) action.callBack(data); 14 | return data; 15 | } 16 | 17 | export default function (LOLYATOwnedTokenURI = initialState.LOLYATOwnedTokenURI, action) { 18 | switch (action.type) { 19 | case types.LOLYAT_TOKEN_URI_SUCCESS: 20 | return fetchOwnedTokenURI(LOLYATOwnedTokenURI, action); 21 | default: 22 | return LOLYATOwnedTokenURI; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /dapp/src/reducers/LOLYATReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | const fetchName = (state, action) => { 5 | return {name: action.result, symbol: state.symbol}; 6 | } 7 | 8 | const fetchSymbol = (state, action) => { 9 | return {name: state.name, symbol: action.result}; 10 | } 11 | 12 | export default function (LOLYAT = initialState.LOLYAT, action) { 13 | switch (action.type) { 14 | case types.LOLYAT_NAME_SUCCESS: 15 | return fetchName(LOLYAT, action); 16 | case types.LOLYAT_SYMBOL_SUCCESS: 17 | return fetchSymbol(LOLYAT, action); 18 | default: 19 | return LOLYAT; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dapp/src/reducers/counterReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | export default function (count = initialState.count, action) { 5 | switch (action.type) { 6 | case types.ADD: 7 | return count+1; 8 | case types.SUB: 9 | return count-1; 10 | default: 11 | return count; 12 | } 13 | } -------------------------------------------------------------------------------- /dapp/src/reducers/fetchingReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/actionTypes'; 2 | import initialState from './initialState'; 3 | 4 | export default function (state = initialState.isFecthing, action) { 5 | switch (action.type) { 6 | case types.FETCHING: 7 | return true; 8 | case types.FETCH_COMPLETE: 9 | return false; 10 | default: 11 | return state; 12 | } 13 | } -------------------------------------------------------------------------------- /dapp/src/reducers/healthReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | const healthStatus = (state, action) => { 5 | return action.result; 6 | } 7 | 8 | export default function (health = initialState.health, action) { 9 | switch (action.type) { 10 | case types.HEALTH_STATUS: 11 | return healthStatus(health, action); 12 | default: 13 | return health; 14 | } 15 | } -------------------------------------------------------------------------------- /dapp/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import count from './counterReducer'; 3 | import system from './systemReducer'; 4 | import isFetching from './fetchingReducer'; 5 | import health from './healthReducer'; 6 | import simpleToken from './simpleTokenReducer'; 7 | import warning from './warningReducer'; 8 | import metaMask from './metaMaskReducer'; 9 | import LOLYAT from './LOLYATReducer'; 10 | import LOLYATOwned from './LOLYATOwnedReducer'; 11 | import LOLYATOwnedTokenURI from './LOLYATOwnedTokenURIReducer'; 12 | 13 | const appReducer = combineReducers({ 14 | count, 15 | system, 16 | isFetching, 17 | health, 18 | simpleToken, 19 | warning, 20 | metaMask, 21 | LOLYAT, 22 | LOLYATOwned, 23 | LOLYATOwnedTokenURI 24 | }); 25 | 26 | const rootReducer = (state, action) => { 27 | return appReducer(state, action); 28 | } 29 | 30 | export default rootReducer; 31 | -------------------------------------------------------------------------------- /dapp/src/reducers/initialState.js: -------------------------------------------------------------------------------- 1 | export default { 2 | isFecthing: false, 3 | count: 0, 4 | system: '', 5 | health: '', 6 | simpleToken: { 7 | name: '', 8 | symbol: '', 9 | decimals: '' 10 | }, 11 | warning: { 12 | message: '', 13 | open: false 14 | }, 15 | metaMask: { 16 | account: null, 17 | network: null 18 | }, 19 | LOLYAT: { 20 | name: 'LOLYAT', 21 | symbol: 'LOW' 22 | }, 23 | LOLYATOwned: [], 24 | LOLYATOwnedTokenURI: {} 25 | }; -------------------------------------------------------------------------------- /dapp/src/reducers/metaMaskReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | const fetchAccount = (state, action) => { 5 | return {account: action.account, network: state.network}; 6 | } 7 | 8 | const fetchNetwork = (state, action) => { 9 | return {account: state.account, network: action.network}; 10 | } 11 | 12 | export default function (metaMask = initialState.metaMask, action) { 13 | switch (action.type) { 14 | case types.METAMASK_ACCOUNT: 15 | return fetchAccount(metaMask, action); 16 | case types.METAMASK_NETWORK: 17 | return fetchNetwork(metaMask, action); 18 | default: 19 | return metaMask; 20 | } 21 | } -------------------------------------------------------------------------------- /dapp/src/reducers/simpleTokenReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | const fetchName = (state, action) => { 5 | return {name: action.result, symbol: state.symbol, decimals: state.decimals}; 6 | } 7 | 8 | const fetchSymbol = (state, action) => { 9 | return {name: state.name, symbol: action.result, decimals: state.decimals}; 10 | } 11 | 12 | const fetchDecimals = (state, action) => { 13 | return {name: state.name, symbol: state.symbol, decimals: action.result}; 14 | } 15 | 16 | export default function (simpleToken = initialState.simpleToken, action) { 17 | switch (action.type) { 18 | case types.SIMPLE_TOKEN_NAME_SUCCESS: 19 | return fetchName(simpleToken, action); 20 | case types.SIMPLE_TOKEN_SYMBOL_SUCCESS: 21 | return fetchSymbol(simpleToken, action); 22 | case types.SIMPLE_TOKEN_DECIMALS_SUCCESS: 23 | return fetchDecimals(simpleToken, action); 24 | default: 25 | return simpleToken; 26 | } 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /dapp/src/reducers/systemReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | const systemError = (state, action) => { 5 | return {message: action.error}; 6 | } 7 | 8 | export default function (system = initialState.system, action) { 9 | switch (action.type) { 10 | case types.SYSTEM_ERROR: 11 | return systemError(system, action); 12 | default: 13 | return system; 14 | } 15 | } -------------------------------------------------------------------------------- /dapp/src/reducers/warningReducer.js: -------------------------------------------------------------------------------- 1 | import initialState from './initialState'; 2 | import * as types from '../constants/actionTypes'; 3 | 4 | const warningOpen = (state, action) => { 5 | return {message: action.message, open: true}; 6 | } 7 | 8 | const warningClose = (state, action) => { 9 | return {message: '', open: false}; 10 | } 11 | 12 | export default function (warning = initialState.warning, action) { 13 | switch (action.type) { 14 | case types.WARNING_OPEN: 15 | return warningOpen(warning, action); 16 | case types.WARNING_CLOSE: 17 | return warningClose(warning, action); 18 | default: 19 | return warning; 20 | } 21 | } -------------------------------------------------------------------------------- /dapp/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | } else { 39 | // Is not local host. Just register service worker 40 | registerValidSW(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /dapp/src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {BrowserRouter as Router, Route} from 'react-router-dom'; 3 | import AppContainer from './containers/AppContainer'; 4 | import Health from './components/Health'; 5 | 6 | export default( 7 | <Router> 8 | <div className="routes"> 9 | <Route exact path="/" component={AppContainer}/> 10 | <Route path="/health" component={Health}/> 11 | </div> 12 | </Router> 13 | ); -------------------------------------------------------------------------------- /dapp/src/sagas/LOLYATOwnedSaga.js: -------------------------------------------------------------------------------- 1 | import { put, call } from 'redux-saga/effects'; 2 | import * as types from '../constants/actionTypes'; 3 | import { 4 | doGetOwnedTokens 5 | } from '../lib/LOLYATService'; 6 | 7 | export function* getLOLYATGetOwnedTokensResultSaga({networkId, address, callBack}) { 8 | try { 9 | yield put({ type: types.FETCHING}); 10 | const LOLYATGetOwnedTokensResult = yield call(doGetOwnedTokens, networkId, address); 11 | // TODO forEach run all the cards 12 | yield put({ type: types.FETCH_COMPLETE}); 13 | yield put({ type: types.LOLYAT_GET_OWNED_TOKENS_SUCCESS, result: LOLYATGetOwnedTokensResult.toString(), callBack}); 14 | } catch (err) { 15 | yield put({ type: types.SYSTEM_ERROR, error: err }); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /dapp/src/sagas/LOLYATOwnedTokenURISaga.js: -------------------------------------------------------------------------------- 1 | import { put, call } from 'redux-saga/effects'; 2 | import * as types from '../constants/actionTypes'; 3 | import { 4 | getTokenURI 5 | } from '../lib/LOLYATService'; 6 | 7 | export function* getLOLYATOwnedTokenURIResultSaga({networkId, tokenId, callBack}) { 8 | try { 9 | yield put({ type: types.FETCHING}); 10 | const LOLYATOwnedTokenURIResult = yield call(getTokenURI, networkId, tokenId); 11 | yield put({ type: types.FETCH_COMPLETE}); 12 | yield put({ type: types.LOLYAT_URI_SUCCESS, result: LOLYATOwnedTokenURIResult, callBack }); 13 | } catch (err) { 14 | yield put({ type: types.SYSTEM_ERROR, error: err }); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /dapp/src/sagas/LOLYATSaga.js: -------------------------------------------------------------------------------- 1 | import { put, call } from 'redux-saga/effects'; 2 | import * as types from '../constants/actionTypes'; 3 | import { 4 | getName, 5 | getSymbol 6 | } from '../lib/LOLYATService'; 7 | 8 | export function* getLOLYATNameResultSaga({networkId}) { 9 | try { 10 | console.log(networkId); 11 | yield put({ type: types.FETCHING}); 12 | const LOLYATNameResult = yield call(getName, networkId); 13 | yield put({ type: types.FETCH_COMPLETE}); 14 | 15 | yield put({ type: types.LOLYAT_NAME_SUCCESS, result: LOLYATNameResult }); 16 | } catch (err) { 17 | yield put({ type: types.SYSTEM_ERROR, error: err }); 18 | } 19 | }; 20 | 21 | export function* getLOLYATSymbolResultSaga({networkId}) { 22 | try { 23 | yield put({ type: types.FETCHING}); 24 | const LOLYATSymbolResult = yield call(getSymbol, networkId); 25 | yield put({ type: types.FETCH_COMPLETE}); 26 | 27 | yield put({ type: types.LOLYAT_SYMBOL_SUCCESS, result: LOLYATSymbolResult }); 28 | } catch (err) { 29 | yield put({ type: types.SYSTEM_ERROR, error: err }); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /dapp/src/sagas/healthSaga.js: -------------------------------------------------------------------------------- 1 | import { put, call } from 'redux-saga/effects'; 2 | import * as types from '../constants/actionTypes'; 3 | import { getHealthResult } from '../apis/api'; 4 | 5 | export function* getHealthResultSaga() { 6 | try { 7 | yield put({ type: types.FETCHING}); 8 | const healthResult = yield call(getHealthResult, null); 9 | yield put({ type: types.FETCH_COMPLETE}); 10 | 11 | yield put({ type: types.HEALTH_STATUS, result: healthResult.status }); 12 | } catch (err) { 13 | yield put({ type: types.SYSTEM_ERROR, error: err }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /dapp/src/sagas/index.js: -------------------------------------------------------------------------------- 1 | import { fork, all } from 'redux-saga/effects'; 2 | import { 3 | watchHealth, 4 | watchSimpleTokenName, 5 | watchSimpleTokenSymbol, 6 | watchSimpleTokenDecimals, 7 | watchLOLYATName, 8 | watchLOLYATSymbol, 9 | watchLOLYATGetOwnedTokens, 10 | watchLOLYATOwnedTokenURI 11 | } from './watcher'; 12 | 13 | export default function* startForman() { 14 | yield all([ 15 | fork(watchHealth), 16 | fork(watchSimpleTokenName), 17 | fork(watchSimpleTokenSymbol), 18 | fork(watchSimpleTokenDecimals), 19 | fork(watchLOLYATName), 20 | fork(watchLOLYATSymbol), 21 | fork(watchLOLYATGetOwnedTokens), 22 | fork(watchLOLYATOwnedTokenURI) 23 | ]); 24 | }; 25 | -------------------------------------------------------------------------------- /dapp/src/sagas/simpleTokenSaga.js: -------------------------------------------------------------------------------- 1 | import { put, call } from 'redux-saga/effects'; 2 | import * as types from '../constants/actionTypes'; 3 | import { 4 | getName, 5 | getSymbol, 6 | getDecimals 7 | } from '../lib/tokenService'; 8 | 9 | export function* getSimpleTokenNameResultSaga({networkId}) { 10 | try { 11 | yield put({ type: types.FETCHING}); 12 | const simpleTokenNameResult = yield call(getName, networkId); 13 | yield put({ type: types.FETCH_COMPLETE}); 14 | 15 | yield put({ type: types.SIMPLE_TOKEN_NAME_SUCCESS, result: simpleTokenNameResult }); 16 | } catch (err) { 17 | yield put({ type: types.SYSTEM_ERROR, error: err }); 18 | } 19 | }; 20 | 21 | export function* getSimpleTokenSymbolResultSaga({networkId}) { 22 | try { 23 | yield put({ type: types.FETCHING}); 24 | const simpleTokenSymbolResult = yield call(getSymbol, networkId); 25 | yield put({ type: types.FETCH_COMPLETE}); 26 | 27 | yield put({ type: types.SIMPLE_TOKEN_SYMBOL_SUCCESS, result: simpleTokenSymbolResult }); 28 | } catch (err) { 29 | yield put({ type: types.SYSTEM_ERROR, error: err }); 30 | } 31 | }; 32 | 33 | export function* getSimpleTokenDecimalsResultSaga({networkId}) { 34 | try { 35 | yield put({ type: types.FETCHING}); 36 | const simpleTokenDecimalsResult = yield call(getDecimals, networkId); 37 | yield put({ type: types.FETCH_COMPLETE}); 38 | const decimal = simpleTokenDecimalsResult.toNumber(); 39 | 40 | yield put({ type: types.SIMPLE_TOKEN_DECIMALS_SUCCESS, result: decimal }); 41 | } catch (err) { 42 | yield put({ type: types.SYSTEM_ERROR, error: err }); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /dapp/src/sagas/watcher.js: -------------------------------------------------------------------------------- 1 | import { takeLatest } from 'redux-saga/effects'; 2 | import * as types from '../constants/actionTypes'; 3 | import { getHealthResultSaga } from './healthSaga'; 4 | import { 5 | getSimpleTokenNameResultSaga, 6 | getSimpleTokenSymbolResultSaga, 7 | getSimpleTokenDecimalsResultSaga, 8 | } from './simpleTokenSaga'; 9 | import { 10 | getLOLYATNameResultSaga, 11 | getLOLYATSymbolResultSaga 12 | } from './LOLYATSaga'; 13 | import { 14 | getLOLYATGetOwnedTokensResultSaga 15 | } from './LOLYATOwnedSaga'; 16 | import { 17 | getLOLYATOwnedTokenURIResultSaga 18 | } from './LOLYATOwnedTokenURISaga'; 19 | 20 | export function* watchHealth() { 21 | yield takeLatest(types.HEALTH, getHealthResultSaga); 22 | } 23 | 24 | export function* watchSimpleTokenName() { 25 | yield takeLatest(types.SIMPLE_TOKEN_NAME, getSimpleTokenNameResultSaga); 26 | } 27 | 28 | export function* watchSimpleTokenSymbol() { 29 | yield takeLatest(types.SIMPLE_TOKEN_SYMBOL, getSimpleTokenSymbolResultSaga); 30 | } 31 | 32 | export function* watchSimpleTokenDecimals() { 33 | yield takeLatest(types.SIMPLE_TOKEN_DECIMALS, getSimpleTokenDecimalsResultSaga); 34 | } 35 | 36 | export function* watchLOLYATName() { 37 | yield takeLatest(types.LOLYAT_TOKEN_NAME, getLOLYATNameResultSaga); 38 | } 39 | 40 | export function* watchLOLYATSymbol() { 41 | yield takeLatest(types.LOLYAT_TOKEN_SYMBOL, getLOLYATSymbolResultSaga); 42 | } 43 | 44 | export function* watchLOLYATGetOwnedTokens() { 45 | yield takeLatest(types.LOLYAT_TOKEN_GET_OWNED_TOKENS, getLOLYATGetOwnedTokensResultSaga); 46 | } 47 | 48 | export function* watchLOLYATOwnedTokenURI() { 49 | yield takeLatest(types.LOLYAT_TOKEN_TOKEN_URI, getLOLYATOwnedTokenURIResultSaga); 50 | } 51 | -------------------------------------------------------------------------------- /dapp/src/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import createSagaMiddleware from 'redux-saga'; 3 | import rootReducer from '../reducers'; 4 | import rootSaga from '../sagas'; 5 | 6 | const configureStore = () => { 7 | const sagaMiddleware = createSagaMiddleware(); 8 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 9 | 10 | return { 11 | ...createStore( 12 | rootReducer, 13 | composeEnhancers(applyMiddleware(sagaMiddleware)) 14 | ), 15 | runSaga: sagaMiddleware.run(rootSaga) 16 | }; 17 | }; 18 | 19 | export default configureStore; -------------------------------------------------------------------------------- /gamehistory.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/gamehistory.gif -------------------------------------------------------------------------------- /herocollection.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/herocollection.gif -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingdayx/LowBit/1a720476ddec372dcb88d151116e851d3df01bdf/icon.png -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_contract.js: -------------------------------------------------------------------------------- 1 | const LowBit = artifacts.require("LowBit"); 2 | const util = require("util"); 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const writeFile = util.promisify(fs.writeFile); 6 | 7 | module.exports = async function (deployer) { 8 | const lowbit = await deployer.deploy( 9 | LowBit, 10 | 0xabb4b0608749e72a3016e424b87af56846f939dc 11 | ); 12 | 13 | console.log("LowBit address: ", LowBit.address); 14 | // const addresses = { 15 | // tokenAddress: CryptoHerosToken.address 16 | // }; 17 | 18 | // await writeFile( 19 | // path.join(__dirname, "..", "dapp", "src", "addresses.json"), 20 | // JSON.stringify(addresses) 21 | // ); 22 | }; 23 | 24 | // https://github.com/ensdomains/ens/blob/master/migrations/2_deploy_contracts.js 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nifty-game", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "zeppelin-solidity": "1.10.0" 8 | }, 9 | "devDependencies": { 10 | "babel-polyfill": "^6.23.0", 11 | "babel-preset-env": "^1.1.8", 12 | "babel-preset-es2015": "^6.18.0", 13 | "babel-preset-stage-2": "^6.18.0", 14 | "babel-preset-stage-3": "^6.17.0", 15 | "babel-register": "^6.23.0", 16 | "classnames": "^2.2.6", 17 | "gsap": "^2.0.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/cryptoHerosToken.test.js: -------------------------------------------------------------------------------- 1 | import assertRevert from "zeppelin-solidity/test/helpers/assertRevert"; 2 | 3 | const CryptoHerosToken = artifacts.require("CryptoHerosToken"); 4 | const CryptoHerosGame = artifacts.require("CryptoHerosGame"); 5 | 6 | contract("CryptoHeros token", accounts => { 7 | let cryptoHerosToken; 8 | let cryptoHerosGame; 9 | 10 | beforeEach(async () => { 11 | cryptoHerosToken = await CryptoHerosToken.deployed(); 12 | console.log(cryptoHerosToken.address); 13 | cryptoHerosGame = await CryptoHerosGame.new(cryptoHerosToken.address); 14 | console.log(cryptoHerosGame.address); 15 | }); 16 | 17 | it("Should make first account an owner on CryptoHerosToken", async () => { 18 | let owner = await cryptoHerosToken.owner(); 19 | assert.equal(owner, accounts[0]); 20 | }); 21 | 22 | it("Should make first account an owner on CryptoHerosGame", async () => { 23 | let owner = await cryptoHerosGame.owner(); 24 | assert.equal(owner, accounts[0]); 25 | }); 26 | 27 | it("Should get contract name", async () => { 28 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 29 | let name = await cryptoHerosToken.name(); 30 | assert.equal(name, "CryptoHerosToken"); 31 | }); 32 | 33 | it("Should get contract symbol", async () => { 34 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 35 | let symbol = await cryptoHerosToken.symbol(); 36 | assert.equal(symbol, "HERO"); 37 | }); 38 | 39 | it("Should get contract owner", async () => { 40 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 41 | let owner = await cryptoHerosToken.owner(); 42 | assert.equal(owner, accounts[0]); 43 | }); 44 | 45 | it("Should init hero", async () => { 46 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 47 | let result = await cryptoHerosToken.initImage('image0'); 48 | assert.equal(result.receipt.status, '0x1'); 49 | let result2 = await cryptoHerosToken.initBackground('background0'); 50 | assert.equal(result2.receipt.status, '0x1'); 51 | let result3 = await cryptoHerosToken.initNumberAndDescription(1, 'description0'); 52 | assert.equal(result3.receipt.status, '0x1'); 53 | let result4 = await cryptoHerosToken.initNumberAndDescription(2, 'description0'); 54 | assert.equal(result4.receipt.status, '0x1'); 55 | let result5 = await cryptoHerosToken.initNumberAndDescription(3, 'description0'); 56 | assert.equal(result5.receipt.status, '0x1'); 57 | //assert.equal(owner, accounts[0]); 58 | }); 59 | 60 | describe("Crypto Heros", () => { 61 | it("Creates crypto heros with specified URI", async () => { 62 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 63 | for (let i=0;i<10;i++) { 64 | await cryptoHerosToken.mint({from: accounts[1], value: web3.toWei(0.02, "ether")}); 65 | } 66 | }); 67 | 68 | it("Get crypto heros token uri", async () => { 69 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 70 | const res = await cryptoHerosToken.getOwnedTokens(accounts[1]); 71 | for(let i = 0; i < res.length; i++) { 72 | const property = await cryptoHerosToken.getTokenProperty(res[i]); 73 | console.log(property); 74 | //assert.equal(property.toNumber() >= 0, true); 75 | } 76 | }); 77 | 78 | it.skip("Get token owner", async () => { 79 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 80 | let owner = await cryptoHerosToken.ownerOf(0); 81 | assert.equal(owner, accounts[1]); 82 | }); 83 | 84 | it.skip("Get owned token", async () => { 85 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 86 | let owner = await cryptoHerosToken.ownerOf(0); 87 | 88 | let ownedTokens = await cryptoHerosToken.getOwnedTokens(owner); 89 | console.log(ownedTokens.toString()); 90 | //assert.equal(ownedTokens, 0); 91 | }) 92 | 93 | it.skip("Should transfer ownership", async () => { 94 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 95 | let other = accounts[1]; 96 | 97 | let owner = await cryptoHerosToken.owner(); 98 | assert.equal(owner, accounts[0]); 99 | await cryptoHerosToken.transferOwnership(other); 100 | let newOwner = await cryptoHerosToken.owner(); 101 | assert.equal(newOwner, accounts[1]); 102 | }); 103 | 104 | 105 | it("Start a game", async () => { 106 | let cryptoHerosToken = await CryptoHerosToken.deployed(); 107 | const res = await cryptoHerosToken.getOwnedTokens(accounts[1]); 108 | //console.log('res: ', res); 109 | console.log('cryptoHerosGame: ', cryptoHerosGame.address); 110 | web3.eth.sendTransaction({from: accounts[0], to: cryptoHerosGame.address, value: web3.toWei(10,"ether"), gas: 2000000}); 111 | console.log(web3.eth.getBalance(accounts[0]).toNumber()); 112 | 113 | for (let i=0;i<res.length;i++) { 114 | const res2 = await cryptoHerosGame.createSingleGame(res[i], {from: accounts[1], value: web3.toWei(0.02, "ether")}); 115 | assert.equal(res2.receipt.status, '0x1'); 116 | // let singleGames = await cryptoHerosGame.singleGames(i); 117 | // console.log('game result: ', singleGames[5].toString() + ' | ' + singleGames[4].toString() + ' | ' + singleGames[1].toString() + ' | ' + singleGames[2].toString()); 118 | } 119 | 120 | const res3 = await cryptoHerosGame.getUserSingleGames(accounts[1]); 121 | for (let i=0;i<res3.length;i++) { 122 | console.log(res3[i].toString()); 123 | let singleGames = await cryptoHerosGame.singleGames(res3[i].toString()); 124 | console.log('game result: ', singleGames[5].toString() + ' | ' + singleGames[4].toString() + ' | ' + singleGames[1].toString() + ' | ' + singleGames[2].toString()); 125 | } 126 | 127 | }); 128 | }); 129 | }); 130 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | const promisify = inner => 2 | new Promise((resolve, reject) => 3 | inner((err, res) => { 4 | if (err) { 5 | reject(err); 6 | } 7 | resolve(res); 8 | }) 9 | ); 10 | 11 | const getBalance = async addr => { 12 | const res = await promisify(cb => web3.eth.getBalance(addr, cb)); 13 | return new web3.BigNumber(res); 14 | }; 15 | 16 | module.exports = { 17 | getBalance 18 | }; 19 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | var HDWalletProvider = require("truffle-hdwallet-provider"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const mnemonic = fs.readFileSync(".secret").toString().trim(); 5 | const infuraKey = "b33e244c7e2f486bb0252feb96b73b7d"; 6 | 7 | module.exports = { 8 | // See <http://truffleframework.com/docs/advanced/configuration> 9 | // to customize your Truffle configuration! 10 | contracts_build_directory: path.join(__dirname, "src/contracts"), 11 | networks: { 12 | ropsten: { 13 | provider: function () { 14 | return new HDWalletProvider( 15 | mnemonic, 16 | `https://ropsten.infura.io/v3/${infuraKey}` 17 | ); 18 | }, 19 | 20 | network_id: 3, // Ropsten's id 21 | gas: 5500000, // Ropsten has a lower block limit than mainnet 22 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 23 | timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 24 | skipDryRun: true, // Skip dry run before migrations? (default: false for public nets ) 25 | }, 26 | kovan: { 27 | provider: () => 28 | new HDWalletProvider( 29 | "f31a0fb40aae450e6f5c2152651b5fef2d9ce9b6156dd8324a3d7fb2e8a14466", 30 | "https://kovan.infura.io/v3/d70678b4d4dc4f3f87f77bab6cd9104d" 31 | ), 32 | network_id: 42, // kovan's id 33 | gas: 3000000, // Ropsten has a lower block limit than mainnet 34 | gasPrice: 10000000000, 35 | //confirmations: 2, // # of confs to wait between deployments. (default: 0) 36 | timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 37 | skipDryRun: true, // Skip dry run before migrations? (default: false for public nets ) 38 | }, 39 | develop: { 40 | host: "localhost", 41 | port: 7545, 42 | network_id: "5777", 43 | }, 44 | bsc_testnet: { 45 | provider: () => 46 | new HDWalletProvider( 47 | mnemonic, 48 | `https://data-seed-prebsc-1-s1.binance.org:8545` 49 | ), 50 | network_id: 97, 51 | confirmations: 10, 52 | timeoutBlocks: 200, 53 | skipDryRun: true, 54 | }, 55 | bsc_mainnet: { 56 | provider: () => 57 | new HDWalletProvider(mnemonic, `https://bsc-dataseed1.binance.org`), 58 | network_id: 56, 59 | confirmations: 10, 60 | timeoutBlocks: 200, 61 | skipDryRun: true, 62 | }, 63 | }, 64 | plugins: ["truffle-plugin-verify"], 65 | api_keys: { etherscan: "KABVKBFMEJ93S65P78CQ1WG71IX5YJYYJJ" }, 66 | 67 | compilers: { 68 | solc: { 69 | version: "0.5.16", // A version or constraint - Ex. "^0.5.0" 70 | // Can also be set to "native" to use a native solc // Use a version obtained through docker 71 | parser: "solcjs", // Leverages solc-js purely for speedy parsing 72 | settings: { 73 | evmVersion: "istanbul", // Default: "petersburg" 74 | }, 75 | }, 76 | }, 77 | }; 78 | --------------------------------------------------------------------------------