본문 바로가기
  • 개발 / 공부 / 일상
BlockChain

(BlockChain) Solidity를 이용한 간단하게 Bakery 예제

by JJeongHyun 2023. 3. 8.
반응형

작업 폴더 생성

  • front / back 폴더생성

각 폴더에 필요로 하는 라이브러리 및 init 설정 ( vscode Terminal )

  • front 
    • yarn create react-app front
    • cd front
    • yarn add web3
  • back
    • cd back
    • npm init -y
    • npm i truffle
    • npm i -D prettier-plugin-solidity
    • npx truffle init

useWeb3.js 생성

  • Custom Hook 생성

solidity 파일 생성 ( Bakery.sol )

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract Bakery {
  mapping(address => uint) public breads;

  function buyBread() public payable {
    require(msg.value >= 10 ** 18);
    breads[msg.sender] += 1;
  }

  function sellBread() public payable {
    breads[msg.sender] -= 1;
    payable(msg.sender).transfer(10 ** 18);
  }

  function getBread() public view returns (uint) {
    return breads[msg.sender];
  }

  function getSender() public view returns (address) {
    return msg.sender;
  }
}
  • CA : Contract Address, 계정이며 지갑 주소 중 하나 이므로 Ether를 보유할 수 있다
  • function payable 옵션
    • payable은 스마트 컨트랙트에게 거래를 하고 싶으면 쓰는 옵션
    • CA주소로 해당 컨트랙의 Balance(잔액)를 확인할 수 있다

migration 내 배포 JS 파일 생성

const Bakery = artifacts.require("Bakery");

module.exports = function (deployer) {
  deployer.deploy(Bakery);
};

컴파일 및 배포

  • npx truffle compile
  • npx truffle migration

front/src내 contract, component 폴더 생성

  • 컴파일, 배포 후에 생성된 build내 JSON 형식의 파일을 가져와서 import 할 수 있도록 넣어둔다

App.js/Bakery.jsx

  • 현재 빵의 개수를 출력
  • contract의 getBread 메서드를 호출
import { useState, useEffect } from "react";
import BakeryContract from "../contracts/Bakery.json";

const Bakery = ({ web3, account }) => {
  const [bread, setBread] = useState(0);

  const initialize = async () => {
    if (!web3) return;

    const networkId = await web3.eth.net.getId();
    const _CA = BakeryContract.networks[networkId].address;
    const abi = BakeryContract.abi;

    const _deployed = new web3.eth.Contract(abi, _CA);

    const _bread = await _deployed.methods.getBread().call({ from: account });
    setBread(_bread);
  };
  useEffect(() => {
    initialize();
  }, []);

  return <div>현재 빵 개수 : {bread}</div>;
};

export default Bakery;

Bakery.jsx

  • buyBread함수 생성 ( 빵을 구매하는 함수 )
  • sellBread함수 생성 ( 빵을 판매하는 함수 )
    • initialize함수내에 있는 deployed, CA를 가져오기 위해 새롭게 state를 생성해서 정의해 준다
import { useState, useEffect } from "react";
import BakeryContract from "../contracts/Bakery.json";

const Bakery = ({ web3, account }) => {
  const [bread, setBread] = useState(0);
  const [deployed, setDeployed] = useState();
  const [CA, setCA] = useState();

  const initialize = async () => {
    if (!web3) return;

    const networkId = await web3.eth.net.getId();
    const _CA = BakeryContract.networks[networkId].address;
    setCA(_CA);
    const abi = BakeryContract.abi;

    const _deployed = new web3.eth.Contract(abi, _CA);
    setDeployed(_deployed);

    const _bread = await _deployed.methods.getBread().call({ from: account });
    setBread(_bread);
  };

  const buyBread = async () => {
    await deployed.methods
      .buyBread()
      .send({ from: account, to: CA, value: web3.utils.toWel("1") });
    const _bread = await deployed.methods.getBread().call({ from: account });
    setBread(_bread);
  };

  const sellBread = async () => {
    await deployed.methods.sellBread().send({ from: account });
    const _bread = await deployed.methods.getBread().call({ from: account });
    setBread(_bread);
  };

  useEffect(() => {
    initialize();
  }, []);

  return (
    <>
      <div>현재 빵 개수 : {bread}</div>
      <button onClick={buyBread}>빵 사기</button>
      <button onClick={sellBread}>빵 팔기</button>
    </>
  );
};

export default Bakery;

 

실행 결과 )

위 코드들을 실행한 결과