Cute Bow Tie Hearts Blinking Pink Pointer

블록체인/스마트 컨트랙트

[스마트 컨트랙트] 물건 사고, 환불하는 dApp 간단히 구현

청포도 에이드 2022. 7. 20. 16:51
728x90

 

사과를 구매하고, 환불할 수 있는 아주아주 간단한 dApp을 구현해보겠다.

 

목차

- 디렉토리 구조

- truffle 백

- 프론트

- 메타마스크 연결

- 물건 사고, 환불해보기

 

 

디렉토리 구조

루트디렉토리
백 디렉토리
프론트

 

Truffle 배포하기

터미널 열고

npx ganache-cli

 

다른 터미널에

mkdir truffle
cd truffle
truffle init

 

truffle/contracts/AppleShop.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

contract AppleShop{
    mapping (address => uint) myApple; //address를 넣어주면 uint(정수)값이 튀어나온다.
	
    function buyApple() public payable{
    	// 사과 구매
        //send, trnafer, call을 이용하여, 이더를 보낼때 Payable이라는 키워드가 필요함.
        myApple[msg.sender] += 1;
        // msg.send : 현재 함수를 호출한사람, 혹은 컨트랙트의 주소
    }

    function sellApple(uint _applePrice) public payable {

        //모든 사과 환불
    
        uint256 refund = myApple[msg.sender] * _applePrice; //가진 apple수 * apple가격
        myApple[msg.sender] = 0; // 가진 사과 개수를 0으로 바꿔줌.
        payable(msg.sender).transfer(refund);
    }

    function getApple() view public returns (uint){
        return myApple[msg.sender];
        // 내가 가진 사과 수 출력
    }
}

 

이더를 보내는 3가지 방법 

이더를 보내는 3가지 방법은 send, transfer, call 이 있다.

이 세가지는 각각의 특징이 있다. 

    1.send : 2300 gas를 소비, 성공여부를 true 또는 false로 리턴한다
    2.transfer : 2300 gas를 소비, 실패시 에러를 발생
    3.call : 가변적인 gas 소비 (gas값 지정 가능), 성공여부를 true 또는 false로 리턴

              재진입(reentrancy) 공격 위험성 있음, 2019년 12월 이후 call 사용을 추천. 

 

 

https://dayone.tistory.com/33

 

솔리디티 강좌 32강 - payable,msg.value, 와 이더를 보내는 3가지 함수 (send, transfer, call)

유튜브를 통해, 쉽고 간편하게 이해 해보아요! 구독/좋아요 해주셔서 감사합니다 :) !! https://youtu.be/r0qa0IVUHVI 안녕하세요 오늘은 payable, msg.value 와 코인보내는 3가지 방법 send, transfer, call..

dayone.tistory.com

 

truffle/migrations/2_deploy.AppleShop.js
const AppleShop = artifacts.require('AppleShop');

module.exports = function (deployer) {
    deployer.deploy(AppleShop);
};
truffle/truffle-config.js

devlopment 주석해제하기.

 

 

 

truffle 디렉토리 안에서

 

truffle migration

하면 배포가 끝난다. (프론트와 메인넷 통신을 통해 백에서 작성한 메서드를 사용할 수 있다.)

 

그러면, build디렉토리 안에 contracts가 생기는데,

리액트를 다운 받고 src안에 그 폴더를 복사해서 넣어준다.(편의를 위해) 

 

 

프론트

 

create-react-app appleshop
npm install web3

 

appleshop/src/App.js
import React from "react";
import useWeb3 from "./Hook/useWeb3";
import AppleShop from "./components/AppleShop";
const App = () => {
    const [web3, account] = useWeb3();

    if (!account) return <>메타 마스크 연결하세요.</>;
    return (
        <div>
            <h2>사과앱</h2>
            <AppleShop web3={web3} account={account}></AppleShop>
        </div>
    );
};

export default App;

 

 

 

appleshop/src/components/AppleShop.jsx

 

import React, { useEffect, useState } from "react";
import AppleShopContract from "../contracts/AppleShop.json";
import Web3 from "web3/dist/web3.min";
const AppleShop = ({ web3, account }) => {
    const [apple, setApple] = useState();
    const [deployed, setDeployed] = useState();

    const buy = async () => {
        //send 작업
        await deployed.methods.buyApple().send({
            from: account,
            to: "0xf525a9837Bb65dCE2572059206Cc9b7F77e6CBf9",
            value: web3.utils.toWei("1", "ether"),
        });
    }; //사기
    const sell = async () => {
        const eth = web3.utils.toWei("1", "ether");
        await deployed.methods.sellApple(eth).send({
            from: account,
            to: "0xf525a9837Bb65dCE2572059206Cc9b7F77e6CBf9",
        });
    }; //환불

    useEffect(() => {
        (async () => {
            if (!web3) return;

            const instance = await new web3.eth.Contract(
                AppleShopContract.abi,
                "0xf525a9837Bb65dCE2572059206Cc9b7F77e6CBf9"
            );

            const currentApple = await instance.methods.getApple().call();
            setApple(currentApple);
            setDeployed(instance);
        })();
    }, []);

    return (
        <div>
            <div>사과 가격 : 1ETH</div>
            <div>내가 가진 사과 : {apple}</div>
            <button onClick={buy}>구매하기</button>
            <div>사과 판매 가격</div>
            <button onClick={sell}>환불</button>
        </div>
    );
};

export default AppleShop;

send안에 들어가는 to값은 CA이고 truffle migration을 하면 터미널에 ca값이 뜨는데, 그 값을 붙여넣어주면된다.

 

 

appleshop/src/Hook/useWeb3.jsx

메타마스크와 연결이 됐는지 확인 해준다.

import React, { useEffect, useState } from "react";
import Web3 from "web3/dist/web3.min";

const useWeb3 = () => {
    const [account, setAccount] = useState(null);
    const [web3, setWeb3] = useState(null);

    useEffect(() => {
        (async () => {
            if (!window.ethereum) return;

            const [address] = await window.ethereum.request({
                method: "eth_requestAccounts",
            });

            setAccount(address);

            const web3 = new Web3(window.ethereum);
            setWeb3(web3);
        })();
    }, []);

    return [web3, account];
};

export default useWeb3;

 

메타마스크 연결하기

 

가나쉬에서 만들어진 test계정 중 첫번째 private key를 메타마스크에 등록하고,

리액트를 npm run start하면, 연결하라는 알림이 자동으로 뜬다.

그때 계정을 선택하고 연결하면 됨.

결과

 

locahost:3000

 

구매하기를 눌러보자.

확인을 누르면 사과 1개를 살 수 있다.(무려 1이더!)

확인을 누르자.

 

새로 고침해주면? 가진 사과의 수가 변했다.

 

이제 환불해보자.

 

확인 누르면?

 

환불이 아주 잘된 것을 확인할 수 있다.

728x90