truffle config.js
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
development 의 host와 portf 위와같이 설정함으로 ganache와 소통할 준비를 한다.
컴파일러는 solc의 버전 설정을 위해 사용된다. 0.8.13 버전사용의 이유는 명확하지는않다.
compilers: {
solc: {
version: "0.8.13", // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
// settings: { // See the solidity docs for advice about optimization and evmVersion
// optimizer: {
// enabled: false,
// runs: 200
// },
// evmVersion: "byzantium"
// }
},
},
react
import { useEffect, useState } from "react";
import useWeb3 from "./hooks/web3.hook";
import abi from "./abi/Baseball.json";
const App = () => {
const { user, web3 } = useWeb3();
const [ticket, setTicket] = useState("0");
const [BaseballContract, SetBaseballContract] = useState(null);
// 우리가 입력해서 매개변수로 요청할 값 우리가 정한 정답
const [value, setValue] = useState("");
// 게임을 한사람들이 얼마나 이더를 쌓아놨는지
// 정답 맞추면 다 그사람 것
const [reward, setReward] = useState("0");
// 게임을 몇판이나 사람들이 진행했는지
const [progress, setProgress] = useState("0");
// 컨트랙트 배포자만 알수있는 답
const [random, setRandom] = useState("000");
// 게임이 진행중인지 여부
const [message, setMessage] = useState("");
// 주인 판단
const [Owner, setOwner] = useState("");
useEffect(() => {
if (web3 !== null) {
if (BaseballContract === null) {
const Baseball = new web3.eth.Contract(
abi,
"0x94832d64A02A7C3d0D3b0F8d574c1274df6d6b22",
{ data: "" }
);
SetBaseballContract(Baseball);
}
}
}, [web3]);
const getTicket = async () => {
if (BaseballContract === null) return;
const result = web3.utils
.toBigInt(await BaseballContract.methods.getTicketPrice().call())
.toString(10);
setTicket(await web3.utils.fromWei(result, "ether"));
};
const getReward = async () => {
if (BaseballContract === null) return;
const result = web3.utils
.toBigInt(await BaseballContract.methods.getRward().call())
.toString(10);
setReward(await web3.utils.fromWei(result, "ether"));
};
const getPlaying = async () => {
const playing = web3.utils
.toBigInt(await BaseballContract.methods.getplaying().call())
.toString(10);
setMessage(playing);
};
const getProgress = async () => {
const progress = web3.utils
.toBigInt(await BaseballContract.methods.getProgress().call())
.toString(10);
setProgress(progress);
};
const getRandom = async () => {
const Random = web3.utils
.toBigInt(
await BaseballContract.methods.getRandom().call({
from: user.account,
})
)
.toString(10);
setRandom(Random);
console.log(Random);
};
const gameStart = async () => {
if (value.length < 3) {
alert("숫자 3자리이상 입력해라");
return;
}
await BaseballContract.methods.gameStart(Number(value)).send({
from: user.account,
value: web3.utils.toWei("5", "ether"),
});
render();
};
const gamerestart = async () => {
console.log("재시작 버튼 클릭");
await BaseballContract.methods.gamerestart().send({
from: user.account,
});
render();
};
const showOwner = async () => {
const Owner = await BaseballContract.methods.showowner().call();
// console.log(web3.utils.toBigInt(Owner).toString(10));
setOwner(Owner);
console.log(Owner);
console.log(user.account);
// Owner;
// render();
};
const render = () => {
getTicket();
getReward();
getPlaying();
getProgress();
getRandom();
showOwner();
};
useEffect(() => {
if (BaseballContract !== null) {
render();
}
}, [BaseballContract]);
if (user.account === null) return "지갑연결하세요";
return (
<>
<div>컨트랙트 주인: {Owner}</div>
<div>플레이유저: {user.account}</div>
<div>티켓 가격 : {ticket}</div>
<div>현재 게임 진행도 : {progress}/5</div>
<div>총 상금 : {reward}</div>
<div>진행 상황 : {message == 0 ? "게임중" : "게임종료"}</div>
<div>어드민인가?{random !== "000" ? random : "어드민이 아니시군요"}</div>
<input
onChange={(e) => {
setValue(e.target.value);
}}
></input>
<button
onClick={() => {
gameStart();
}}
>
게임시작
</button>
{message == 1 ? (
<button
onClick={() => {
gamerestart();
}}
>
게임 재시작
</button>
) : null}
</>
);
};
export default App;
baseball.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Baseball{
// 컨트랙트를 배포한 사람
// 컨트랙트 소유자
// 컴퓨터가 숫자 3개를 랜덤하게 뽑고
// 이 숫자를 플레이어들이 맞추는 게임
// 숫자를 입력해서 값을 비교하고 틀리면 그사람은 이더를 CA에 보낸다.
// CA플레이어들이 게임을 하면서 모인 이더를 최종적으로 맞춘 사람에게 보상으로 주고
// 게임 플레이 횟수가 있는데
// 게임 플레이 횟수가 있는데 횟수만큼 진행했는데 못맞췄다
// 보상은 아무도 못받고 컨트랙트 소유자가 이더를 가져간다.
// 컨트랙트 배포자
address private owner;
// 게임의 횟수
// constant 구문을 추가하면 상태를 변경하지 않을 상태변수 상수다
uint256 private constant GAME_COUNT =5;
// ticketa 게임을 하고싶으면 지불해야하는 이더
uint256 private ticket= 5 ether;
// 정답의 값을 담아놓을 변수
// 컴퓨터가 정할 랜덤값
// 3자리수의 숫자
uint256 private random;
// 게임의 진행도
uint256 private progress;
// 총모여있는 상금
uint256 private reward;
// 게임의 현재 상태
enum GameStatus{
playing, // 0
GameOver // 1
}
// 최초의 상태값은 0
// 게임 플레이중 playing
GameStatus gameStatus ; // 0
// 컨트랙트 생성자
// 컨트랙트 배포될때 딱 한번 실행되는데
// msg는 컨트랙트를 실행시킨사람의 정보를 담고있다.? dd 실행시킨사람
constructor(){
// 최초에 딱 한번 배포자가 상태변수에 담기고
owner=msg.sender;
// keccak256 : 솔리디티에서 랜덤값을 뽑을때 사용 매개변수를 해시값으로 변경해준다. SHA-3 이걸 사용한다. 해시값만들때
// block
// abi.encodePacked : 매개변수로 전달된 내용들을 가지고 바이트 배열로 만들어준다.
random = uint256(keccak256(abi.encodePacked(block.timestamp,block.difficulty,block.coinbase,block.number)));
// 큰숫가자가 나오는데
// 이숫자를 가지고 나머지 연산을 통해 원하는 자리수의 숫자를 구하자
// 100 ~999 까지의 범위를 지정을 할거다.
random = (random % 900 )+100;
// 100 ~999의 랜덤값
}
// 유저의 값을 받아서 비교를 통해 값이 맞는지 게임의 정답을 맞췄는지 구할 함수
// payable 이더로 돈을 내고 해야하기떄문
function gameStart(uint256 _value) public payable{
require(progress<GAME_COUNT,"GameOver");
require(msg.value==ticket,"ticket amount error(5 ether)");
require((_value>=100)&& (_value<1000),"_value should be (99<_value<1000)");
progress +=1;
if(_value==random)
{
// 게임 끝 정답
// CA의 잔액이 보상 만큼 있는지 검사
require(reward<=address(this).balance);
payable(msg.sender).transfer(address(this).balance);
reward= 0;
// gameStatus 상태가 상수값 1로 들어감
// 1은 게임이 끝났다는 얘기
gameStatus = GameStatus.GameOver;
}else if(progress==GAME_COUNT)
{
// 게임 끝 정답
// CA의 잔액이 보상 만큼 있는지 검사
require(reward<=address(this).balance);
payable(owner).transfer(address(this).balance);
reward= 0;
// gameStatus 상태가 상수값 1로 들어감
// 1은 게임이 끝났다는 얘기
gameStatus = GameStatus.GameOver;
}
else{
reward += msg.value;
}
}
// 지금 현재 쌓인 보상을 보여줄 함수
function getRward()public view returns(uint256){
return reward;
}
// 게임이 얼마나 진행됬는지 보여줄함수
function getProgress()public view returns(uint256){
return progress;
}
// 테켓의 금액을 보여줄함수
// 지정자 제한자
function getTicketPrice() public view returns(uint256){
return ticket;
}
// 어드민 모드
// 정답을 확인하는 함수
function getRandom() public returns(uint256) {
// msg.sender는 payable달려잇어야한다.
// require(owner==msg.sender,"admin");
if (address(owner)== address(msg.sender))
{ return random;
}else{
return 0;
}
}
// 게임중인지 확인할 함수
function getplaying()public view returns(uint256) {
// 게임이 진행되고있는 상수값이 0
uint256 Playing = 0;
if((gameStatus !=GameStatus.playing) || (progress == GAME_COUNT))
{
// 게임이 끝났다.
Playing = 1;
}
return Playing;
}
function showowner()public view returns(address){
return owner;
}
function gamerestart()public payable{
random= uint256(keccak256(abi.encodePacked(block.timestamp,block.difficulty,block.coinbase,block.number)));
random = (random % 900 )+100;
progress=0;
reward=0;
}
}
sol 문법
sql 문법만 있는것은 아니다.
enum
require
mapping 등 이있다.
enum은 특정한 이름으로 숫자를 대체해서 가독성이 용이하게만들어진것이다.
enum gameStatus{
playing,
Gameover
}
이 구조체를 가지고 서언된 변수는 0번쨰 인수를 기준으로 선언된다.
gameStatus gamestatus 로 선언하면 gamestatus 는 playing, gameover 를 가질수있다.
require은 처음에는 if문이랑 같다고 생각했는데 다르다.
먼저 if문은 해당 조건문에 참이면 {}안의 문법을 실행하고 아니면 else를 실행하거나 다음 컨텍스트로 넘어간다.
require은 좀 더 선제적인 성격을 띤다고 본다. if의 분기점에서 만약 참이 아니라면 else또는 다음 컨텍스트가아니라
바로 이전 분기점으로 다시 돌아가는것이다.
mapping은 객체의 형태로 이해하면 좀더 적합한거같다 .약간 마트료 시카
이진 트리 문제를 풀다보면 treenode의 형태로 된 class를 가지고 여러개의 객체를 생성해서 면제를 해결하곤하는데
mapping은 그 클래스의 구조를 만드는것과 유사하다. 생각한다 아직 잘모른다.
예시와 다를수있지만 내가 이해한것은
이동수단{
오토바이:
{
이륜:
사륜:
}
자동차:{
스포츠카:{}
세단 : {}
}
}
이런것처럼 어떤 형태로 구성되는지를 표현하는것이라 생각한다면 이해하기 수월할것이라 여겨진다.
payable
`payable`은 Solidity에서 사용되는 키워드로, 스마트 계약 함수가 이더를 받을 수 있는지 여부를 나타냅니다. `payable` 키워드를 사용하여 함수를 선언하면 해당 함수는 이더를 받을 수 있으며, 그렇지 않으면 이더를 받을 수 없습니다.
`payable` 함수와 `non-payable` 함수 간의 주요 차이점은 다음과 같습니다:
1. **이더 수신**: `payable` 함수는 스마트 계약으로 이더를 송금할 수 있습니다. 이는 함수가 이더를 받을 수 있는 상태라는 것을 의미합니다. 반면 `non-payable` 함수는 이더를 받을 수 없습니다.
2. **가스 비용**: 이더를 전송하거나 받을 때 가스 비용이 발생합니다. `payable` 함수를 호출할 때는 이더를 전송하는 가스 비용을 지불해야 합니다.
3. **상태 변경**: `payable` 함수를 호출하면 스마트 계약의 상태가 변경될 수 있습니다. `non-payable` 함수는 이더를 받을 수 없으므로 주로 계약의 데이터를 읽는 데 사용됩니다.
예를 들어, 스마트 계약의 "입금" 함수는 `payable` 함수여야 하며, 이더를 계약으로 입금할 수 있게 해야 합니다. 반면 "잔액 조회" 함수는 `non-payable` 함수로 이더를 받을 필요가 없으므로 `non-payable` 함수로 구현할 수 있습니다.
간단한 예제로 설명하면:
```solidity
// 이더를 받을 수 있는 payable 함수
function deposit() public payable {
// 이더를 입금하는 로직
}
// 이더를 받을 필요가 없는 non-payable 함수
function getBalance() public view returns (uint256) {
// 계약 잔액 조회 로직
}
```
따라서 `payable` 및 `non-payable` 키워드를 사용하여 스마트 계약 함수의 동작을 정의하고 이더를 관리할 수 있습니다.