[이더리움] Truffle 실습

민수·2023년 5월 24일
0

Truffle

npx truffle init

컴파일

  1. 컴파일 할 solidity 파일 작성

contracts/Counter.sol

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

contract Counter {
    uint256 value;

    constructor() {}

    function getValue() public view returns(uint256) {
        return value;
    }

    function increment() public {
        value += 1;
    }

    function decrement() public {
        value -= 1;
    }
}

contract Hello {
    string public hi = 'Hello Solidity';
}
  1. truffle-config.js solc 버전 수정

Ganache CLI v6.12.2 (ganache-core: 2.13.2) 기준으로 solc 버전이 0.8.18 이하일 때만 컴파일한 파일을 배포할 때 오류가 나지 않는다.

npx truffle compile
Compiling your contracts...
===========================
> Compiling ./contracts/Counter.sol
> Artifacts written to /home/cloudcoke/my/blockchain/etherium/230524_p/build/contracts
> Compiled successfully using:
   - solc: 0.8.18+commit.87f61d96.Emscripten.clang
  1. build/contracts에 생긴 Counter.json, Hello.json 확인

컴파일을 진행하면 contract 마다 json 파일이 생성된다.

이 파일에 배포할 때 필요한 것들이 들어있다.

배포

  1. truffle network 설정
    배포를 하기 위해 truffle-config.js에서 networks를 설정해줘야 한다.

ganache를 이용해서 로컬 환경에서 배포를 진행하기 위해 development 부분을 주석해제를 해준다.

networks: {
     development: {
      host: "127.0.0.1", // Localhost (default: none)
      port: 8545, // Standard Ethereum port (default: none)
      network_id: "*", // Any network (default: none)
    },
}
  1. migration 파일 작성

참고

migration : 마이그레이션은 Ethereum 네트워크에 계약을 배포하는 데 도움이 되는 JavaScript 파일

migrations/1_deploy_counter.js

마이그레이션이 성공적으로 실행되었는지를 기록하기 위해 파일 이름 맨 앞에 번호를 적어준다.

const Counter = artifacts.require("Counter")
const Hello = artifacts.require("Hello")
// require의 인자로 solidity 파일 이름이 아닌 계약 정의 이름을 전달해 주어야 한다.
// 컴파일을 진행한 뒤 생성된 json 파일의 이름을 전달해 주면 된다.

module.exports = (developer) => {
  developer.deploy(Counter)
  developer.deploy(Hello)
}
// 매개변수로 받는 developer는 truffle이 배포를 위해 노드에 요청을 날릴 때 해당 노드에 존재하는 EOA 중 첫 번째 EOA를 넣어준다고 볼 수 있다.
  1. ganache 네트워크 열기
npx ganache-cli -h 0.0.0.0
  1. migrations 디렉터리 내에 있는 모든 마이그레이션 실행
npx truffle migration
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.


Starting migrations...
======================
> Network name:    'development'
> Network id:      1684908856831
> Block gas limit: 6721975 (0x6691b7)


1_deploy_counter.js
===================

   Deploying 'Counter'
   -------------------
   > transaction hash:    0x12a6d666b307bfbe301fbe9e3e1a25a5a8289a26e90afb6b18a6b5760b9f0582
   > Blocks: 0            Seconds: 0
   > contract address:    0x25Aee7d838028EDE29c5aAa10eFF614aEeab1711
   > block number:        1
   > block timestamp:     1684909164
   > account:             0xd48b29AD5361057027A22ECb266A99135600dBFD
   > balance:             99.99704426
   > gas used:            147787 (0x2414b)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00295574 ETH


   Deploying 'Hello'
   -----------------
   > transaction hash:    0x723e1df7b248506ea835b1a9d473cd59e36867ba8e679da244a4db11349ec367
   > Blocks: 0            Seconds: 0
   > contract address:    0x081E64644144029988f7e49bA20EfEbaF321130f
   > block number:        2
   > block timestamp:     1684909164
   > account:             0xd48b29AD5361057027A22ECb266A99135600dBFD
   > balance:             99.9929363
   > gas used:            205398 (0x32256)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00410796 ETH

   > Saving artifacts
   -------------------------------------
   > Total cost:           0.0070637 ETH

Summary
=======
> Total deployments:   2
> Final cost:          0.0070637 ETH

ganache의 결과를 보면 두 개의 블록이 생성된 걸 확인할 수 있다.

eth_sendTransaction

  Transaction: 0x12a6d666b307bfbe301fbe9e3e1a25a5a8289a26e90afb6b18a6b5760b9f0582
  Contract created: 0x25aee7d838028ede29c5aaa10eff614aeeab1711
  Gas usage: 147787
  Block Number: 1
  Block Time: Wed May 24 2023 15:19:24 GMT+0900 (Korean Standard Time)
eth_sendTransaction

  Transaction: 0x723e1df7b248506ea835b1a9d473cd59e36867ba8e679da244a4db11349ec367
  Contract created: 0x081e64644144029988f7e49ba20efebaf321130f
  Gas usage: 205398
  Block Number: 2
  Block Time: Wed May 24 2023 15:19:24 GMT+0900 (Korean Standard Time)

truffle console

console로 테스트를 진행해 볼 수 있다.

npx truffle console
Counter.address
# '0x25Aee7d838028EDE29c5aAa10eFF614aEeab1711'

Hello.address
# '0x081E64644144029988f7e49bA20EfEbaF321130f'
Counter.deployed().then(instance => counter = instance)

window 객체에 counter라는 변수를 만들어 Counter의 인스턴스 값을 넣어준다.

counter.getValue()
BN { negative: 0, words: [ 0, <1 empty item> ], length: 1, red: null }

Big Number 타입의 결과물이 나온다.

counter.increment()
{
  tx: '0x9cca4cf59fbe9fb65401fae43d725b3d4d33f5e710764478770c180d9997ecd6',
  receipt: {
    transactionHash: '0x9cca4cf59fbe9fb65401fae43d725b3d4d33f5e710764478770c180d9997ecd6',
    transactionIndex: 0,
    blockHash: '0x880fd811422252021bdbea7719f0f6c434ad09d142869b523ff2021ff333c821',
    blockNumber: 3,
    from: '0xd48b29ad5361057027a22ecb266a99135600dbfd',
    to: '0x25aee7d838028ede29c5aaa10eff614aeeab1711',
    gasUsed: 42245,
    cumulativeGasUsed: 42245,
    contractAddress: null,
    logs: [],
    status: true,
    logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    rawLogs: []
  },
  logs: []
}

트랜잭션 내용이 나온다.

counter.getValue()

value 값을 다시 조회하면 1이 증가해 있는 걸 확인할 수 있다.

BN { negative: 0, words: [ 1, <1 empty item> ], length: 1, red: null }

truffle을 이용해 smart contract를 배포하고 실행 테스트까지 한 다음 웹에서 사용할 수 있도록 한다.

react에서 smart contract에 배포한 Counter 사용하기

react 프로젝트 생성

npx create-react-app front

web3 라이브러리 설치

cd front
npm install web3

truffle로 빌드한 파일 복사하기

mkdir src/contracts
cp ../build/contracts/Counter.json src/contracts

react 코드 작성

src/hooks/useWeb3.jsx

import Web3 from "web3"
import { useEffect, useState } from "react"

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

  const init = async () => {
    const [account] = await window.ethereum.request({
      method: "eth_requestAccounts",
    }) // EOA 가져오기

    const web3 = new Web3(window.ethereum)
    setNetId(await web3.eth.net.getId()) // 네트워크 ID 가져오기

    setAccount(account)
    setWeb3(web3)
  }

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

    init()
  }, [])

  return [account, web3, netId]
}

export default useWeb3

src/pages/Counter.jsx

import { useState, useEffect } from "react"
import CounterContract from "../contracts/Counter.json"

const Counter = ({ account, web3, netId }) => {
  const [count, setCount] = useState(0)
  const [deployed, setDeployed] = useState(null)

  const get = async () => {
    if (deployed === null) return alert("deployed가 없음")

    const value = await deployed.methods.getValue().call()
    setCount(value)
  }

  const increment = async () => {
    if (deployed === null) return alert("deployed가 없음")

    await deployed.methods.increment().send({
      from: account,
    })

    get()
  }

  const decrement = async () => {
    if (deployed === null) return alert("deployed가 없음")

    await deployed.methods.decrement().send({
      from: account,
    })

    get()
  }

  useEffect(() => {
    if (web3 === null || account === null) return

    const Deployed = new web3.eth.Contract(CounterContract.abi, CounterContract.networks[netId].address)
    setDeployed(Deployed)

    Deployed.methods
      .getValue()
      .call()
      .then((value) => setCount(value))
  }, [])

  return (
    <>
      <div>
        <h2>Counter : {count}</h2>
        <button onClick={increment}>+</button>
        <button onClick={decrement}>-</button>
      </div>
    </>
  )
}

export default Counter

src/App.jsx

import Counter from "./pages/Counter"
import useWeb3 from "./hooks/useWeb3"

const App = () => {
  const [account, web3, netId] = useWeb3()

  if (!account) return <>메타마스크를 연결하고 사용이 가능합니다.</>

  return (
    <>
      <h1>Counter</h1>
      <Counter account={account} web3={web3} netId={netId} />
    </>
  )
}

export default App

0개의 댓글