$ npm -g install create-react-app
을 통해 create-react-app을 설치한다.
$ create-react-app [프로젝트명]
을 입력하여 리액트 프로젝트를 만든다.
$ npm i web3
를 통해 web3를 설치한다.
https://medium.com/valist/how-to-connect-web3-js-to-metamask-in-2020-fee2b2edf58a
에 어떻게 메타마스크를 web3에 연결하는지 잘 나와있다. 평상시 많이 바뀌므로 검색해서 쓰는 습관을 들이도록 하자.
이렇게쓰라고 나오는데 아무리 찾아봐도 안나온다.....
터미널 창에 다음과 같이 입력한다.
$ npx truffle console
$ web3.eth.getAccounts()
그러면 아무거나 나오는 값을 복사한다.
그리고 다음과 같이 입력한다.
$ web3.eth.sendTransaction({from:'방금 복사한 값',to:'현재 내 메타마스크',value:10000000000000000000})
function App() {
const initWeb3 = async () =>{
if(window.ethereum){
var web3 = new Web3(window.ethereum);
console.log('윈도우야 메타 마스크를 연결하니 이게 실행되네')
console.log(web3)
try{
await window.ethereum.enable();
}catch (error){
console.log(`error ouccur ${error}`)
}
}
//legacy dapp browers..
else if(window.web3){
//이게 실행이 됨
var web3 = new Web3(Web3.curentProvider);
// console.log(await web3.eth.getAccounts());
}
else{
console.log('너 이더리움 없어 메타마스크라도 깔아라....')
}
let accounts = await web3.eth.getAccounts();//여기서 왜 안되지?
console.log(accounts);
// const Web3 = require("web3");
// const ethEnabled = () => {
// if (window.web3) {
// let web3 = new Web3(window.web3.currentProvider);
// window.ethereum.enable();
// console.log(web3);
// return true;
// }
// return false;
// }
}
useEffect(async()=>{
await initWeb3();
},[]);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
원래 여기서 최신 버전이 있으니 그걸 적용해서 하라고 했는데 아무리 찾아봐도 없는 것 같아 그냥 따라 적었다. 그러면서 메타마스크가 있다면 window.ethereum
이 실행되는 것을 알 수 있었다. 그리고 되는 곳에서 var을 사용해줘야지만 지갑의 주소가 나오는 것을 알 수 있었다...ㅜㅜ
스마트 컨트랙트 폴더로 이동하여서 다시한번 스마트 컨트랙트를 다시 배포한다.
$ truffle migrate --reset
그러면 이러한 화면이 뜬다. 여기서 contract address를 복사하자.
0x43C466923FfFFa2d9B8a1Bbba294Fed9a3214b86
그리고 build>contracts>Lottery.json파일에서 abi 를 복사한다. 그리고 다음의 사이트에 접속한다. https://www.textfixer.com/tools/remove-line-breaks.php
그리고 내용을 붙여넣어준다.
그리고 그걸 vs코드에 넣어준다.
스마트 컨트랙트 만들 때 가장 먼저 해야하는 것이 smart contract 객체를 만드는 일이다.
let lotteryContract = new web3.eth.Contract(lotteryABI, lotteryAddress);
let pot = await lotteryContract.methods.getPot().call();
console.log('pot:',pot);
let owner = await lotteryContract.methods.owner().call();
console.log('owner:', owner);
그럼 다음과 같이 console창에 내용이 뜸을 알 수 있다.
그리고 그 바로 밑에 이제 복권ㅇ르 사는 행위를 넣어보자.
lotteryContract.methods.betAndDistribute('0xcd').send({from:accounts, value:5000000000000000, gas:300000})
그러고 이제 저장하고 돌아가보면 메타마스크 창이 뜨는 것을 알 수 있다.
let nonce = await web3.eth.getTransactionCount(accounts[0]);
lotteryContract.methods.betAndDistribute('0xcd').send({from:accounts[0], value:5000000000000000, gas:300000, nonce:nonce});
이것도 추가해준다.
call: 스마트 컨트랙트의 값만 읽어옴. read만 할 수 있다. 변화시키거나 영향을 미치지 못함.
이벤트는 어떻게 찍을 수 있는가+ 이벤트 로그들을 어떻게 가져오는가에 대해서 알아보도록 하자.
filter:이벤트를 캐치한다.
getPastEvents 와 websocket 부분에서 사용할 수 있다.
해당 이벤트에 해당하는 것만 골라오는 메소드이다.
아까 했던 밑부분에 위의내용을 추가해준다.
블록의 처음부터 끝까지 나온 BET을 가져오라는 내용이다.
지금까지 3번 일어났음을 알 수 있다.
여기서 address는 smartcontract address임을 알 수 있다. 그리고 'event'는 log의 내용이 나옴을 알 수 있다. raw 값을 해석한 것이 returnValues이다.
subscribe("BET")
이런식으로 원하는 이벤트를 구독을 할 수 있다.ws를 이용해 polling을 하겠다.
부트 스트랩을 이용할 것이다.
$ npm i bootstrap
https://en.wikipedia.org/wiki/Standard_52-card_deck 에서 카드덱을 찾아서 넣으면 된다.
import logo from './logo.svg';
import './App.css';
import React,{useEffect,useState} from 'react';
import Web3 from 'web3'
function App() {
let lotteryAddress = '0x43C466923FfFFa2d9B8a1Bbba294Fed9a3214b86';
let lotteryABI = [ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": true, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "BET", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "DRAW", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "FAIL", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "REFUND", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "WIN", "type": "event" }, { "inputs": [], "name": "answerForTest", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [], "name": "owner", "outputs": [ { "internalType": "address payable", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [], "name": "getPot", "outputs": [ { "internalType": "uint256", "name": "pot", "type": "uint256" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "name": "betAndDistribute", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "payable", "type": "function", "payable": true }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "name": "bet", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "payable", "type": "function", "payable": true }, { "inputs": [], "name": "distribute", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "bytes32", "name": "answer", "type": "bytes32" } ], "name": "setAnswerForTest", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "internalType": "bytes32", "name": "answer", "type": "bytes32" } ], "name": "isMatch", "outputs": [ { "internalType": "enum Lottery.BettingResult", "name": "", "type": "uint8" } ], "stateMutability": "pure", "type": "function", "constant": true }, { "inputs": [ { "internalType": "uint256", "name": "index", "type": "uint256" } ], "name": "getBetInfo", "outputs": [ { "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" }, { "internalType": "address", "name": "bettor", "type": "address" }, { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "stateMutability": "view", "type": "function", "constant": true } ]
const [state,setState]=useState({
betRecords:[],
winRecords:[],
failRecords:[],
pot:'0',
challenges:['A','B'],
finalRecords:[{
bettor:'어쩌구 저쩌구',
index:'0',
challenges:'ab',
answer:'ab',
targetBlockNumber:'10',
pot:'0'
}]
})
const initWeb3 = async () =>{
if(window.ethereum){
var web3 = new Web3(window.ethereum);
console.log('윈도우야 메타 마스크를 연결하니 이게 실행되네')
console.log(web3)
try{
await window.ethereum.enable();
}catch (error){
console.log(`error ouccur ${error}`)
}
}
//legacy dapp browers..
else if(window.web3){
//이게 실행이 됨
var web3 = new Web3(Web3.curentProvider);
// console.log(await web3.eth.getAccounts());
}
else{
console.log('너 이더리움 없어 메타마스크라도 깔아라....')
}
let accounts = await web3.eth.getAccounts();//여기서 왜 안되지?
console.log(accounts);
//스마트 컨트랙트 만들 때 가장 먼저 해야하는 것이 smart contract 객체를 만드는 일이다.
let lotteryContract = new web3.eth.Contract(lotteryABI, lotteryAddress);
let pot = await lotteryContract.methods.getPot().call();
console.log('pot:',pot);
let owner = await lotteryContract.methods.owner().call();
console.log('owner:', owner);
let nonce = await web3.eth.getTransactionCount(accounts[0]);
lotteryContract.methods.betAndDistribute('0xcd').send({from:accounts[0], value:5000000000000000, gas:300000, nonce:nonce});
let records = [];
let events = await lotteryContract.getPastEvents('BET',{fromBlock:0, toBlock:'latest'});
console.log('events',events);
//블록의 처음부터 끝까지 나온 BET을 가져와라
// const Web3 = require("web3");
// const ethEnabled = () => {
// if (window.web3) {
// let web3 = new Web3(window.web3.currentProvider);
// window.ethereum.enable();
// console.log(web3);
// return true;
// }
// return false;
// }
}
useEffect(async()=>{
await initWeb3();
},[]);
const getCard = (_Character,_cardStyle)=>{
let _card='';
if(_Character === 'A'){
_card = '🂡'
}
if(_Character === 'B'){
_card = '🂱 '
}
if(_Character === 'C'){
_card = '🃁 '
}
if(_Character === 'D'){
_card = '🃑 '
}
return (
<button className={_cardStyle}>
<div className = "card-body text-center">
<p className = "card-text"></p>
<p className = "card-text text-center" style={{fontSize:300}}>{_card}</p>
<p className = "card-text"></p>
</div>
</button>
)
}
return (
<div className="App">
<div className = "container">
<div className = "jumbotron">
<h1>Current Pot : {state.pot}</h1>
<p>Lottery</p>
<p>Lottery tutorial</p>
<p>Your Bet</p>
<p>{state.challenges[0]} {state.challenges[1]}</p>
</div>
</div>
{/*card section */}
<div className = "container">
<div className = "card-group">
{getCard('A','dard bg-primary')}
{getCard('B','dard bg-warning')}
{getCard('C','dard bg-danger')}
{getCard('D','dard bg-success')}
</div>
</div>
<br/>
<div className = "container">
<button className = "btn btn-danger btn-lg">BET!</button>
</div>
<br/>
<div className = "container">
<table className = "table table-dark table-striped">
<thread>
<tr>
<th>Index</th>
<th>Address</th>
<th>Challenge</th>
<th>Answer</th>
<th>Pot</th>
<th>Status</th>
<th>AnswerBlockNumber</th>
</tr>
</thread>
<tbody>
{
state.finalRecords.map((record,index)=>{
return(
<tr key = {index}>
<td>{record.index}</td>
<td>{record.bettor}</td>
<td>{record.challenges}</td>
<td>{record.answer}</td>
<td>{record.pot}</td>
<td>{record.win}</td>
<td>{record.targetBlockNumber}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
</div>
);
}
export default App;
DATA부분에 보면 cd가 되어있는 것이 우리가 배팅을 한 것의 내용이다.
0x부터 그 전까지의 내용은 함수(function) 시그니쳐 내용이다. id 를 주는 것이다.
import logo from './logo.svg';
import './App.css';
import React,{useEffect,useState,useReducer} from 'react';
import Web3 from 'web3'
const reducer = (state,action)=>{
switch(action.type){
case "INIT":{
let {web3,Instance,account}=action;
return {
...state,web3,Instance,account
}
}
case "GETPOT": {
let {pot}= action;
return {
...state,pot
}
}
}
}
function App() {
let lotteryAddress = '0x43C466923FfFFa2d9B8a1Bbba294Fed9a3214b86';
let lotteryABI = [ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": true, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "BET", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "DRAW", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "FAIL", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "REFUND", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "WIN", "type": "event" }, { "inputs": [], "name": "answerForTest", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [], "name": "owner", "outputs": [ { "internalType": "address payable", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [], "name": "getPot", "outputs": [ { "internalType": "uint256", "name": "pot", "type": "uint256" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "name": "betAndDistribute", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "payable", "type": "function", "payable": true }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "name": "bet", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "payable", "type": "function", "payable": true }, { "inputs": [], "name": "distribute", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "bytes32", "name": "answer", "type": "bytes32" } ], "name": "setAnswerForTest", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "internalType": "bytes32", "name": "answer", "type": "bytes32" } ], "name": "isMatch", "outputs": [ { "internalType": "enum Lottery.BettingResult", "name": "", "type": "uint8" } ], "stateMutability": "pure", "type": "function", "constant": true }, { "inputs": [ { "internalType": "uint256", "name": "index", "type": "uint256" } ], "name": "getBetInfo", "outputs": [ { "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" }, { "internalType": "address", "name": "bettor", "type": "address" }, { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "stateMutability": "view", "type": "function", "constant": true } ]
let bytecode = "0x60806040526000600560006101000a81548160ff02191690831515021790555034801561002b57600080fd5b5033600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506114e48061007c6000396000f3fe6080604052600436106100865760003560e01c80638da5cb5b116100595780638da5cb5b1461020157806399a167d714610258578063e16ea857146102e1578063e4fc6b6d14610349578063f4b46f5b1461036057610086565b8063403c9fa81461008b5780637009fa36146100b657806379141f801461010957806384f7e4f0146101d6575b600080fd5b34801561009757600080fd5b506100a06103c8565b6040518082815260200191505060405180910390f35b3480156100c257600080fd5b506100ef600480360360208110156100d957600080fd5b81019080803590602001909291905050506103d2565b604051808215151515815260200191505060405180910390f35b34801561011557600080fd5b506101426004803603602081101561012c57600080fd5b810190808035906020019092919050505061048a565b604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001935050505060405180910390f35b3480156101e257600080fd5b506101eb61058f565b6040518082815260200191505060405180910390f35b34801561020d57600080fd5b50610216610595565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561026457600080fd5b506102bd6004803603604081101561027b57600080fd5b8101908080357effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803590602001909291905050506105bb565b604051808260028111156102cd57fe5b60ff16815260200191505060405180910390f35b61032f600480360360208110156102f757600080fd5b8101908080357effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610896565b604051808215151515815260200191505060405180910390f35b34801561035557600080fd5b5061035e6108b3565b005b6103ae6004803603602081101561037657600080fd5b8101908080357effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610f8d565b604051808215151515815260200191505060405180910390f35b6000600454905090565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461047a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b815260200180611484602b913960400191505060405180910390fd5b8160068190555060019050919050565b600080600061049761142a565b60026000868152602001908152602001600020604051806060016040529081600082015481526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160149054906101000a900460f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815250509050806000015193508060200151925080604001519150509193909250565b60065481565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008083905060008490506000846000602081106105d557fe5b1a60f81b90506000856000602081106105ea57fe5b1a60f81b90506004847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c93506004847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b93506004827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c91506004827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b91506004837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b92506004837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c92506004817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b90506004817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c9050837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480156107ce5750827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b156107e0576001945050505050610890565b837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806108755750827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b15610887576002945050505050610890565b60009450505050505b92915050565b60006108a182610f8d565b506108aa6108b3565b60019050919050565b6000806108be61142a565b60008060015494505b600054851015610f7f5760026000868152602001908152602001600020604051806060016040529081600082015481526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160149054906101000a900460f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152505092506109b98360000151611141565b9150600060028111156109c857fe5b8260028111156109d457fe5b1415610e3b5760006109e98460000151611191565b90506109f98460400151826105bb565b915060016002811115610a0857fe5b826002811115610a1457fe5b1415610b6d57610a3384602001516611c37937e08000600454016111b9565b945060006004819055507f8219079e2d6c1192fb0ff7f78e6faaf5528ad6687e69749205d87bd4b156912b86856020015187876040015185600060208110610a7757fe5b1a60f81b8960000151604051808781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001828152602001965050505050505060405180910390a15b60006002811115610b7a57fe5b826002811115610b8657fe5b1415610cd4576611c37937e080006004600082825401925050819055507f3b19d607433249d2ebc766ae82ca3848e9c064f1febb5147bc6e5b21d0adebc58685602001516000876040015185600060208110610bde57fe5b1a60f81b8960000151604051808781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001828152602001965050505050505060405180910390a15b600280811115610ce057fe5b826002811115610cec57fe5b1415610e3957610d0784602001516611c37937e080006111b9565b94507f72ec2e949e4fad9380f9d5db3e2ed0e71cf22c51d8d66424508bdc761a3f4b0e86856020015187876040015185600060208110610d4357fe5b1a60f81b8960000151604051808781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001828152602001965050505050505060405180910390a15b505b60016002811115610e4857fe5b826002811115610e5457fe5b1415610e5f57610f7f565b600280811115610e6b57fe5b826002811115610e7757fe5b1415610f6857610e9283602001516611c37937e080006111b9565b93507f59c0185881271a0f53d43e6ab9310091408f9e0ff9ae2512613de800f26b8de48584602001518686604001518760000151604051808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020018281526020019550505050505060405180910390a15b610f7185611282565b5084806001019550506108c7565b846001819055505050505050565b60006611c37937e08000341461100b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4e6f7420656e6f7567682045544800000000000000000000000000000000000081525060200191505060405180910390fd5b611014826112e6565b611086576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4661696c20746f206164642061206e65772042657420496e666f00000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f100791de9f40bf2d56ffa6dc5597d2fd0b2703ea70bc7548cd74c04f5d215ab760016000540334856003430160405180858152602001848152602001837effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200182815260200194505050505060405180910390a260019050919050565b600081431180156111555750816101000143105b15611163576000905061118c565b814311611173576001905061118c565b61010082014310611187576002905061118c565b600290505b919050565b6000600560009054906101000a900460ff166111af576006546111b2565b81405b9050919050565b60008060009050600081840390508473ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561120d573d6000803e3d6000fd5b50600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050158015611276573d6000803e3d6000fd5b50809250505092915050565b6000600260008381526020019081526020016000206000808201600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160146101000a81549060ff0219169055505060019050919050565b60006112f061142a565b33816020019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600343018160000181815250508281604001907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815250508060026000805481526020019081526020016000206000820151816000015560208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160146101000a81548160ff021916908360f81c021790555090505060008081548092919060010191905055506001915050919050565b604051806060016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152509056fe4f6e6c79206f776e65722063616e207365742074686520616e7377657220666f722074657374206d6f6465a2646970667358221220efb6c16501351c0526935debc12ba06f64e0240bb8bf03c20a1cb7338c956f7064736f6c63430006000033"
const initialState={
betRecords:[],
winRecords:[],
failRecords:[],
pot:'0',
challenges:['A','B'],
finalRecords:[{
bettor:'어쩌구 저쩌구',
index:'0',
challenges:'ab',
answer:'ab',
targetBlockNumber:'10',
pot:'0'
}],
Instance:null,
account:null,
web3:null}
const [state,dispatch]=useReducer(reducer,initialState);
const bet = async()=>{
let {challenges,web3,Instance,account}=state;
let challenge = '0x'+challenges[0].toLowerCase()+challenges[1].toLowerCase();
let nonce = await web3.eth.getTransactionCount(account[0]);
Instance.methods.betAndDistribute('0xcd').send({from:account[0], value:5000000000000000, gas:300000, nonce:nonce});
}
const initWeb3 = async () =>{
// const contract = require('@truffle/contract');
if(window.ethereum){
var web3 = new Web3(window.ethereum);
console.log('윈도우야 메타 마스크를 연결하니 이게 실행되네')
console.log(web3)
try{
await window.ethereum.enable();
}catch (error){
console.log(`error ouccur ${error}`)
}
}
//legacy dapp browers..
else if(window.web3){
//이게 실행이 됨
var web3 = new Web3(Web3.curentProvider);
// console.log(await web3.eth.getAccounts());
}
else{
console.log('너 이더리움 없어 메타마스크라도 깔아라....')
}
let accounts = await web3.eth.getAccounts();//여기서 왜 안되지?
console.log(accounts);
//스마트 컨트랙트 만들 때 가장 먼저 해야하는 것이 smart contract 객체를 만드는 일이다.
let lotteryContract = new web3.eth.Contract(lotteryABI, lotteryAddress);
console.log('로테리컨트랙트',lotteryContract)
// let pot = await lotteryContract.methods.getPot().call();
// console.log('pot:',pot);
let owner = await lotteryContract.methods.owner().call();
console.log('owner:', owner);
let records = [];
let events = await lotteryContract.getPastEvents('BET',{fromBlock:0, toBlock:'latest'});
console.log('events',events);
//블록의 처음부터 끝까지 나온 BET을 가져와라
// const Web3 = require("web3");
// const ethEnabled = () => {
// if (window.web3) {
// let web3 = new Web3(window.web3.currentProvider);
// window.ethereum.enable();
// console.log(web3);
// return true;
// }
// return false;
// }
dispatch({type:'INIT',web3,account:accounts,Instance:lotteryContract})
}
const getCard = (_Character,_cardStyle)=>{
let _card='';
if(_Character === 'A'){
_card = '🂡'
}
if(_Character === 'B'){
_card = '🂱'
}
if(_Character === 'C'){
_card = '🃁'
}
if(_Character === 'D'){
_card = '🃑'
}
return (
<button className={_cardStyle}>
<div className = "card-body text-center">
<p className = "card-text"></p>
<p className = "card-text text-center" style={{fontSize:300}}>{_card}</p>
<p className = "card-text"></p>
</div>
</button>
)
}
const getPot = async()=>{
let {Instance,web3}=state;
console.log('Instance',Instance);
let pot = await Instance.methods.getPot().call();
let potString = web3.utils.fromWei(pot.toString(),'ether');
//eth단위로 변환시켜줌
console.log('pot',potString);
dispatch({type:"GETPOT",pot:potString});
}
const pollData = async()=>{
await getPot();
}
useEffect(async()=>{
await initWeb3();
await pollData();
// await getPot();
},[]);
return (
<div className="App">
<div className = "container">
<div className = "jumbotron">
<h1>Current Pot : {state.pot}</h1>
<p>Lottery</p>
<p>Lottery tutorial</p>
<p>Your Bet</p>
<p>{state.challenges[0]} {state.challenges[1]}</p>
</div>
</div>
{/*card section */}
<div className = "container">
<div className = "card-group">
{getCard('A','dard bg-primary')}
{getCard('B','dard bg-warning')}
{getCard('C','dard bg-danger')}
{getCard('D','dard bg-success')}
</div>
</div>
<br/>
<div className = "container">
<button className = "btn btn-danger btn-lg" onClick={bet}>BET!</button>
</div>
<br/>
<div className = "container">
<table className = "table table-dark table-striped">
<thead>
<tr>
<th>Index</th>
<th>Address</th>
<th>Challenge</th>
<th>Answer</th>
<th>Pot</th>
<th>Status</th>
<th>AnswerBlockNumber</th>
</tr>
</thead>
<tbody>
{
state.finalRecords.map((record,index)=>{
return(
<tr key = {index}>
<td>{record.index}</td>
<td>{record.bettor}</td>
<td>{record.challenges}</td>
<td>{record.answer}</td>
<td>{record.pot}</td>
<td>{record.win}</td>
<td>{record.targetBlockNumber}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
</div>
);
}
export default App;
카드를 클릭했을 때 배팅하는 것이 달라지는 UI를 만드는 것이다.
const bet = async()=>{
let {challenges,web3,Instance,account}=state;
let challenge = '0x'+challenges[0].toLowerCase()+challenges[1].toLowerCase();
let nonce = await web3.eth.getTransactionCount(account[0]);
Instance.methods.betAndDistribute(challenge).send({from:account[0], value:5000000000000000, gas:300000, nonce:nonce})
.on('transactionHash',(hash)=>{
console.log('HASH값 :',hash);//그냥 해시값 찾아봤다.
});
}
이벤트 찍기
const getBetEvents = async(Instance)=>{
let records = [];
let events = await Instance.getPastEvents('BET',{fromBlock:0, toBlock:'latest'});
for(let i=0; i<events.length;i+=1){
const record = {};
record.index = parseInt(events[i].returnValues.index,10).toString();//hex값으로 오는 경우도 있기에 바꿔준다.
record.bettor = events[i].returnValues.bettor;
record.betBlockNumber = events[i].blockNumber;
record.targetBlockNumber = events[i].returnValues.answerBlockNumber.toString();
record.challenges = events[i].returnValues.challenges;
record.win = 'Not Revealed';
record.answer = '0x00';
records.unshift(record);
}
console.log(records);
dispatch({type:"GETBETEVENT",records})
//블록의 처음부터 끝까지 나온 BET을 가져와라
}
const getWinEvents = async(Instance)=>{
let records = [];
let events = await Instance.getPastEvents('WIN',{fromBlock:0, toBlock:'latest'});
for(let i=0; i<events.length;i+=1){
const record = {};
record.index = parseInt(events[i].returnValues.index,10).toString();//hex값으로 오는 경우도 있기에 바꿔준다.
record.amount = parseInt(events[i].returnValues.amount,10).toString();
records.unshift(record);
}
console.log('winEvent',records);
dispatch({type:"WINEVENT",records})
//블록의 처음부터 끝까지 나온 BET을 가져와라
}
정답은 0x00이므로 00을 선택한 뒤 4개의 다른 bet을 누른다. 그 다음에 새로고침을 해서 보면 다음과 같이 나온다.
그리고 potmoney는 다시 0이 되었다.
const getFailEvents = async(Instance)=>{
let records = [];
let events = await Instance.getPastEvents('FAIL',{fromBlock:0, toBlock:'latest'});
for(let i=0; i<events.length;i+=1){
const record = {};
record.index = parseInt(events[i].returnValues.index,10).toString();//hex값으로 오는 경우도 있기에 바꿔준다.
record.answer = events[i].returnValues.answer;
records.unshift(record);
}
console.log('FailEvent',records);
dispatch({type:"FAILEVENT",records})
//블록의 처음부터 끝까지 나온 BET을 가져와라
}
정답이 중요하기에 정답 레코드를 받는다.
여기서 4개가 Not Revealed처리가 되었다. 그 이유는 필터로 이벤트를 처리함에 있다. 필터로 이벤트를 처리할 때에는 1컴펌이 되어야 한다.
import 'bootstrap/dist/css/bootstrap.css';
import React,{useEffect,useReducer} from 'react';
import Web3 from 'web3'
const reducer = (state,action)=>{
switch(action.type){
case "INIT":{
let {web3,Instance,account}=action;
return {
...state,web3,Instance,account,flag:true
}
}
case "GETPOT": {
let {pot}= action;
return {
...state,pot,
}
}
case "CLICKCARD":{
let _Character = action.data;
let {challenges} = action;
console.log('clickcard',_Character);
return {
...state,challenges:[challenges,_Character]
}
}
case "GETBETEVENT":{
let {records}=action;
return{
...state,betRecords:records
}
}
case "WINEVENT":{
let {records} = action;
return {
...state,winRecords:records,flag1:!state.flag1
}
}
case "FAILEVENT":{
let {records}=action;
return{
...state,failRecords:records,flag2:!state.flag2
}
}
case "FINALRECORD":{
let {records}=action;
return{
...state,finalRecords:records
}
}
}
}
function App() {
let lotteryAddress = '0x5eE960B277574C090212fA92fa7b19BBDc8B913A';
let lotteryABI =[ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": true, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "BET", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "DRAW", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "FAIL", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "REFUND", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, { "indexed": false, "internalType": "address", "name": "bettor", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": false, "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "indexed": false, "internalType": "bytes1", "name": "answer", "type": "bytes1" }, { "indexed": false, "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" } ], "name": "WIN", "type": "event" }, { "inputs": [], "name": "answerForTest", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "owner", "outputs": [ { "internalType": "address payable", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getPot", "outputs": [ { "internalType": "uint256", "name": "pot", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "name": "betAndDistribute", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "payable", "type": "function" }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "name": "bet", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "distribute", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "bytes32", "name": "answer", "type": "bytes32" } ], "name": "setAnswerForTest", "outputs": [ { "internalType": "bool", "name": "result", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "bytes1", "name": "challenges", "type": "bytes1" }, { "internalType": "bytes32", "name": "answer", "type": "bytes32" } ], "name": "isMatch", "outputs": [ { "internalType": "enum Lottery.BettingResult", "name": "", "type": "uint8" } ], "stateMutability": "pure", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "index", "type": "uint256" } ], "name": "getBetInfo", "outputs": [ { "internalType": "uint256", "name": "answerBlockNumber", "type": "uint256" }, { "internalType": "address", "name": "bettor", "type": "address" }, { "internalType": "bytes1", "name": "challenges", "type": "bytes1" } ], "stateMutability": "view", "type": "function" } ];
const initialState={
betRecords:[],
winRecords:[],
failRecords:[],
pot:'0',
challenges:['A','B'],
finalRecords:[{
bettor:'어쩌구 저쩌구',
index:'0',
challenges:'ab',
answer:'ab',
targetBlockNumber:'10',
pot:'0'
}],
Instance:null,
account:null,
web3:null,
flag1:false,
flag2:false,
}
const [state,dispatch]=useReducer(reducer,initialState);
const bet = async()=>{
let {challenges,web3,Instance,account}=state;
let challenge = '0x'+challenges[0].toLowerCase()+challenges[1].toLowerCase();
let nonce = await web3.eth.getTransactionCount(account[0]);
Instance.methods.betAndDistribute(challenge).send({from:account[0], value:5000000000000000, gas:300000, nonce:nonce})
.on('transactionHash',(hash)=>{
console.log('HASH값 :',hash);//그냥 해시값 찾아봤다.
});
}
const makeFinalRecords=()=>{
let {web3}=state;
console.log('makeFinal들어옴')
//win이랑 Fail인 애들을 전부다 매칭시키는 것임
let f=0, w=0;
const records = [...state.betRecords];
console.log('records다 ',records)
for(let i=0; i<state.betRecords.length;i++){
console.log("원래 인덱스");
if(state.winRecords.length>0 && state.betRecords[i].index === state.winRecords[w].index){
//이겼을때
console.log('이긴거 들어옴')
records[i].win = 'WIN';
records[i].answer = records[i].challenges;
records[i].pot = web3.utils.fromWei(state.winRecords[w].amount,'ether');
if(state.winRecords.length-1>w){w++;}
} else if(state.failRecords.length>0 && state.betRecords[i].index === state.failRecords[f].index){
console.log("진짜 얜 왜 안되?")
records[i].win = 'FAIL';
records[i].answer = state.failRecords[f].answer;
records[i].pot = 0;
if(state.failRecords.length-1>f)f++;
} else {
records[i].answer = 'Not Revealed';
}
}
console.log("FINAL",records);
clearInterval();
dispatch({type:"FINALRECORD",records})
}
const getBetEvents = async(Instance)=>{
let records = [];
let events = await Instance.getPastEvents('BET',{fromBlock:0, toBlock:'latest'});
for(let i=0; i<events.length;i+=1){
const record = {};
record.index = parseInt(events[i].returnValues.index,10).toString();//hex값으로 오는 경우도 있기에 바꿔준다.
record.bettor = events[i].returnValues.bettor.slice(0,4) + '...' + events[i].returnValues.bettor.slice(40,42);
record.betBlockNumber = events[i].blockNumber;
record.targetBlockNumber = events[i].returnValues.answerBlockNumber.toString();
record.challenges = events[i].returnValues.challenges;
record.win = 'Not Revealed';
record.answer = '0x00';
records.unshift(record);
}
console.log('getbetEvent',records);
clearInterval();
dispatch({type:"GETBETEVENT",records})
//블록의 처음부터 끝까지 나온 BET을 가져와라
}
const getWinEvents = async(Instance)=>{
let records = [];
let events = await Instance.getPastEvents('WIN',{fromBlock:0, toBlock:'latest'});
for(let i=0; i<events.length;i+=1){
const record = {};
record.index = parseInt(events[i].returnValues.index,10).toString();//hex값으로 오는 경우도 있기에 바꿔준다.
record.amount = parseInt(events[i].returnValues.amount,10).toString();
records.unshift(record);
}
console.log('winEvent',records);
clearInterval();
dispatch({type:"WINEVENT",records})
//블록의 처음부터 끝까지 나온 BET을 가져와라
}
const getFailEvents = async(Instance)=>{
let records = [];
let events = await Instance.getPastEvents('FAIL',{fromBlock:0, toBlock:'latest'});
for(let i=0; i<events.length;i+=1){
const record = {};
record.index = parseInt(events[i].returnValues.index,10).toString();//hex값으로 오는 경우도 있기에 바꿔준다.
record.answer = events[i].returnValues.answer;
records.unshift(record);
}
console.log('FailEvent',records);
dispatch({type:"FAILEVENT",records})
//블록의 처음부터 끝까지 나온 BET을 가져와라
}
async function dataPoll(web3,Instance){
await getPot(web3,Instance);
await getBetEvents(Instance);
await getWinEvents(Instance);
await getFailEvents(Instance);
}
const initWeb3 = async () =>{
// const contract = require('@truffle/contract');
let web3
if(window.ethereum){
web3 = new Web3(window.ethereum);
console.log(web3)
try{
await window.ethereum.enable();
}catch (error){
console.log(`error ouccur ${error}`)
}
} else if(window.web3) { //legacy dapp browers..
//이게 실행이 됨
web3 = new Web3(Web3.curentProvider);
// console.log(await web3.eth.getAccounts());
} else {
console.log('너 이더리움 없어 메타마스크라도 깔아라....')
}
let accounts = await web3.eth.getAccounts();//여기서 왜 안되지?
//스마트 컨트랙트 만들 때 가장 먼저 해야하는 것이 smart contract 객체를 만드는 일이다.
let lotteryContract = new web3.eth.Contract(lotteryABI, lotteryAddress); // console.log('로테리컨트랙트',lotteryContract)
// let pot = await lotteryContract.methods.getPot().call();
// console.log('pot:',pot);
let owner = await lotteryContract.methods.owner().call();
console.log('owner:', owner);
dispatch({ type: 'INIT', web3, account: accounts, Instance: lotteryContract })
// await getPot();
// await getPot();
// await getPot();
// await getBetEvents();
let interval = setInterval(() => {
dataPoll(web3,lotteryContract);
}, 1000);
// let interval = setInterval(() => {
// getPot(web3,lotteryContract);
// }, 1000);
// await getBetEvents(lotteryContract);
// await getWinEvents(lotteryContract);
// await getFailEvents(lotteryContract);
}
const onClickCard=(Character)=>{
dispatch({type:"CLICKCARD",data:Character,challenges:state.challenges[1]})
}
const getCard = (_Character,_cardStyle)=>{
let _card='';
if(_Character === 'A'){
_card = '🂡'
}
if(_Character === 'B'){
_card = '🂱'
}
if(_Character === 'C'){
_card = '🃁'
}
if(_Character === '0'){
_card = '🃑'
}
return (
<button className={_cardStyle} onClick = {()=>{onClickCard(_Character)}}>
<div className = "card-body text-center">
<p className = "card-text"></p>
<p className = "card-text text-center" style={{fontSize:300}}>{_card}</p>
<p className = "card-text"></p>
</div>
</button>
)
}
const getPot = async(web3,Instance)=>{
let pot = await Instance.methods.getPot().call();
let potString = web3.utils.fromWei(pot.toString(),'ether');
//eth단위로 변환시켜줌
clearInterval()
dispatch({type:"GETPOT",pot:potString});
}
const pollData = async()=>{
await initWeb3();
}
useEffect(()=>{
pollData();
},[]);
useEffect(()=>{
makeFinalRecords();
},[state.flag2])
return (
<div className="App">
<div className = "container">
<div className = "jumbotron">
<h1>Current Pot : {state.pot}</h1>
<p>Lottery</p>
<p>Lottery tutorial</p>
<p>Your Bet</p>
<p>{state.challenges[0]} {state.challenges[1]}</p>
</div>
</div>
{/*card section */}
<div className = "container">
<div className = "card-group">
{getCard('A','dard bg-primary')}
{getCard('B','dard bg-warning')}
{getCard('C','dard bg-danger')}
{getCard('0','dard bg-success')}
</div>
</div>
<br/>
<div className = "container">
<button className = "btn btn-danger btn-lg" onClick={bet}>BET!</button>
</div>
<br/>
<div className = "container">
<table className = "table table-dark table-striped">
<thead>
<tr>
<th>Index</th>
<th>Address</th>
<th>Challenge</th>
<th>Answer</th>
<th>Pot</th>
<th>Status</th>
<th>AnswerBlockNumber</th>
</tr>
</thead>
<tbody>
{
state.finalRecords.map((record,index)=>{
return(
<tr key = {index}>
<td>{record.index}</td>
<td>{record.bettor}</td>
<td>{record.challenges}</td>
<td>{record.answer}</td>
<td>{record.pot}</td>
<td>{record.win}</td>
<td>{record.targetBlockNumber}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
</div>
);
}
export default App;
클래스형을 함수형으로 고치다보니까 dispatch로 인한 문제가 생겼다. 화면을 그리지 못해서 난감했던 시간들이 꽤 길었다. 그래도 포기하지 않고 마저 했고, 또한 교수님께서 같이 1시간동안 붙잡고 해결하는데 도움을 주셨다.... @truffle/contract로는 도저히 bet함수가 구현이 안되어서 다시 돌아왔지만... 그래도 비동기통신에 대해 더 확실하게 알 수 있었다. 그리고 지난번 프로젝트때 websocket으로 인한 통신을 함으로써 useEffect를 더 잘 사용할 수 있었다.
지금 새벽 2시 30분.... 그래도 해결하니까 기분은 좋다!!😁
안녕하세요 너무 좋은 내용의 글 잘 봤습니다 :)
최근 web3js를 배우면서 HDWalletProvider를 사용하여 혼자서 실습을 하다가, 리액트와 함께 배포하는 과정을 실습하려고 하는데요. 궁금한 부분이 생겨 구글링을 하다가 여쭤보고싶은 게 있어서 댓글을 달게 되었습니다! 답글 달아주시면 감사하겠습니다 :)
지금까지는 제 스스로의 계정만을 넣어서 web3 = new Web3(provider)로 해서 저의 계정을 가져왔다면, 배포하여 사용자의 메타마스크 계정과 연결하기 위해서는 web3 = new Web3(window.ethereum) => web3.eth.getAccount() 과정을 거쳐 실제 사용자의 메타마스크 계정과 연동할 수 있는 것인가요?
단순하게, 원래 인퓨라로 제공되었던 provider를 위 과정을 통해 metamask가 provider가 되고, 인퓨라 노드를 통해서 배포하고, 컨트랙트 내 함수를 호출하고 했던 일련의 과정이 metamask를 통해 실행하게 되는건가요?
위 과정을 통해 metamask를 provider로 설정한 후에는, contract.methods.call() / send() 등 이전에 사용했던 web3 함수들을 그대로 적용해서 사용해도 똑같이 작동하는것인가요?