분류 기타

풀 스택 이더리움 개발을 위한 완벽한 가이드

컨텐츠 정보

  • 조회 366 (작성일 )

본문

React, Ethers.js, Solidity 및 Hardhat으로 풀 스택 dApp 구축.


이 프로젝트의 코드는 여기에 있습니다.


저는 최근에 개발자 관계 엔지니어로 Edge & Node에 합류했으며 이더리움을 통한 스마트 계약 개발에 대해 더 깊이 파고 들었습니다. 나는 Solidity로 풀 스택 dApp을 구축하기 위한 최고의 스택이라고 생각하는 것을 결정했습니다.


▶︎ Client Framework - React

▶︎ Ethereum development environment - Hardhat

▶︎ Ethereum Web Client Library - Ethers.js

▶︎ API layer - The Graph Protocol


이것을 배우는 동안 내가 만났던 문제는 각각의 것들에 대해 꽤 좋은 문서가 있지만,이 모든 것들을 함께 모으고 서로 어떻게 작동하는지 이해하는 방법에 대해서는 실제로 아무것도 없다는 것입니다. . scaffold-eth (Ethers, Hardhat 및 The Graph도 포함)와 같은 정말 좋은 상용구가 있지만, 막 시작하는 사람들에게는 너무 많은 것을 선택할 수 있습니다.


최신 리소스, 라이브러리 및 도구를 사용하여 풀 스택 이더리움 앱을 빌드하는 방법을 보여주는 엔드 투 엔드 가이드를 원했습니다.


내가 관심을 보인 것은 다음과 같습니다.


  1. 로컬, 테스트 및 메인 넷에 이더리움 스마트 계약을 생성, 배포 및 테스트하는 방법
  2. 로컬, 테스트 및 프로덕션 환경 / 네트워크간에 전환하는 방법
  3. React, Vue, Svelte 또는 Angular와 같은 프런트 엔드의 다양한 환경을 사용하여 계약에 연결하고 상호 작용하는 방법

이 모든 것을 알아 내고 정말 만족스러웠던 스택을 가지고 가는 데 시간을 보낸 후, 다른 사람들을 위해서만이 아니라 이 스택을 사용하여 풀 스택 이더리움 앱을 빌드하고 테스트하는 방법을 작성하는 것이 좋을 것이라고 생각했습니다. 이 스택에 관심이 있을지 모르지만 나중에 참조 할 수 있도록 나 자신을 위한 것입니다. 이것이 그 참조입니다.


The pieces 


우리가 사용할 주요 부분과 이들이 스택에 어떻게 들어가는 지 살펴 보겠습니다.


1. Ethereum development environment 


스마트 계약을 구축 할 때 라이브 환경을 다루지 않고도 계약을 배포하고, 테스트를 실행하고, Solidity 코드를 디버깅 할 수 있는 방법이 필요합니다.


또한 Solidity 코드를 클라이언트 측 애플리케이션 (여기서는 React 앱)에서 실행할 수 있는 코드로 컴파일하는 방법이 필요합니다. 이것이 어떻게 작동하는지 조금 후에 더 배울 것입니다.


Hardhat은 풀 스택 개발을 위해 설계된 이더리움 개발 환경 및 프레임 워크이며 이 튜토리얼에서 사용할 프레임 워크입니다.


생태계의 다른 유사한 도구는 GanacheTruffle입니다.


2. Ethereum Web Client Library 


React 앱에서 배포 된 스마트 계약과 상호 작용할 수 있는 방법이 필요합니다. 데이터를 읽고 새로운 트랜잭션을 보내는 방법이 필요합니다.


ethers.js는 React, Vue, Angular 또는 Svelte와 같은 클라이언트 측 JavaScript 애플리케이션에서 Ethereum Blockchain 및 생태계와 상호 작용하기 위한 완전하고 컴팩트 한 라이브러리를 목표로 합니다. 우리가 사용할 라이브러리입니다.


생태계에서 또 다른 인기 있는 옵션은 web3.js입니다.


3. Metamask 


Metamask는 계정 관리를 처리하고 현재 사용자를 블록 체인에 연결하는 데 도움이 됩니다. MetaMask를 사용하면 사용자가 사이트 컨텍스트에서 격리하면서 몇 가지 다른 방법으로 계정과 키를 관리 할 수 ​​있습니다.


사용자가 MetaMask 지갑을 연결하면 개발자는 Web3 호환 브라우저 사용자 (예 : MetaMask 사용자)를 식별하는 전 세계적으로 사용 가능한 Ethereum API (window.ethereum)와 상호 작용할 수 있으며 트랜잭션 서명을 요청할 때마다 MetaMask를 사용할 수 있습니다. 사용자에게 가능한 한 이해할 수 있는 방법으로 안내합니다.


4. React 


React는 웹 애플리케이션, 사용자 인터페이스 및 UI 구성 요소를 구축하기 위한 프런트 엔드 JavaScript 라이브러리입니다. Facebook과 많은 개별 개발자 및 회사에서 유지 관리합니다.


React와 Next.js, Gatsby, Redwood, Blitz.js 등과 같은 메타 프레임 워크의 대규모 에코 시스템은 기존 SPA, 정적 사이트 생성기, 서버 측 렌더링 및 이 세 가지 조합을 포함한 모든 유형의 배포 대상을 지원합니다. React는 겉보기에 프런트 엔드 공간을 계속 지배하고 있으며 적어도 가까운 장래에 계속 그렇게 할 것이라고 생각합니다.


5. The Graph 


Ethereum과 같은 블록 체인에 구축 된 대부분의 앱의 경우 체인에서 직접 데이터를 읽는 것이 어렵고 시간이 많이 걸리므로 사람과 회사가 자체 중앙 집중식 인덱싱 서버를 구축하고 이러한 서버에서 API 요청을 제공하는 것을 보곤 했습니다. 이를 위해서는 많은 엔지니어링 및 하드웨어 리소스가 필요하며 분산화에 필요한 보안 속성이 손상됩니다.


Graph는 블록 체인 데이터를 쿼리하기 위한 인덱싱 프로토콜로, 완전히 분산 된 애플리케이션을 생성하고 이 문제를 해결하여 앱이 사용할 수 있는 풍부한 GraphQL 쿼리 레이어를 노출합니다. 이 가이드에서는 앱에 대한 하위 그래프를 빌드하지 않지만 향후 자습서에서 그렇게 할 것입니다.


우리가 구축 할 것 


이 튜토리얼에서는 몇 가지 기본 스마트 계약을 구축, 배포 및 연결합니다.


  1. 이더리움 블록 체인에서 메시지 생성 및 업데이트 계약
  2. 토큰을 채굴 한 다음 계약 소유자가 다른 사람에게 토큰을 보내고 토큰 잔액을 읽고 새 토큰의 소유자가 토큰을 다른 사람에게 보낼 수 있도록 하는 계약입니다.

또한 사용자가 다음을 수행 할 수 있는 React 프런트 엔드를 구축 할 것입니다.


  1. 블록 체인에 배포 된 계약의 인사말 읽기
  2. 인사말 업데이트
  3. 새로 발행 된 토큰을 해당 주소에서 다른 주소로 보냅니다.
  4. 누군가 토큰을 받으면 다른 사람에게도 토큰을 보낼 수 있습니다.
  5. 블록 체인에 배포 된 계약에서 토큰 잔고 읽기

전제 조건 

  1. 로컬 머신에 설치된 Node.js
  2. 브라우저에 설치된 MetaMask Chrome 확장 프로그램

전체 튜토리얼을 위해 테스트 네트워크에서 가짜 / 테스트 이더를 사용할 것이므로 이 가이드를 위해 이더리움을 소유 할 필요가 없습니다.


시작하기 


시작하려면 새 React 애플리케이션을 만듭니다.


npx create-react-app react-dapp


다음으로 새 디렉토리로 변경하고 NPM 또는 Yarn을 사용하여 ethers.jshardhat을 설치합니다.


npm install ethers hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers


이더리움 개발 환경 설치 및 구성 


다음으로, Hardhat으로 새로운 이더리움 개발 환경을 초기화합니다.


npx hardhat

? What do you want to do? Create a sample project
? Hardhat project root: <Choose default path>


이제 루트 디렉토리에 생성 된 다음 아티팩트가 표시됩니다.


hardhat.config.js-전체 Hardhat 설정 (즉, 구성, 플러그인 및 사용자 지정 작업)이 이 파일에 포함되어 있습니다.

scripts-실행시 스마트 계약을 배포 할 sample-script.js라는 스크립트가 포함 된 폴더

test-예제 테스트 스크립트가 포함 된 폴더

contracts-예제 이더리움 스마트 계약이 들어있는 폴더


MetaMask 구성 문제로 인해 HardHat 구성의 체인 ID를 1337로 업데이트해야 합니다. 컴파일 된 계약의 아티팩트 위치도 React 앱의 src 디렉토리에 있도록 업데이트해야 합니다.


이러한 업데이트를 수행하려면 hardhat.config.js를 열고 module.exports를 다음과 같이 업데이트하십시오.


module.exports = {
  solidity: "0.8.3",
  paths: {
    artifacts: './src/artifacts',
  },
  networks: {
    hardhat: {
      chainId: 1337
    }
  }
};


우리의 스마트 계약 


다음으로 contracts/Greeter.sol에서 제공되는 예제 계약을 살펴 보겠습니다.


//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.0;

import "hardhat/console.sol";


contract Greeter {
  string greeting;

  constructor(string memory _greeting) {
    console.log("Deploying a Greeter with greeting:", _greeting);
    greeting = _greeting;
  }

  function greet() public view returns (string memory) {
    return greeting;
  }

  function setGreeting(string memory _greeting) public {
    console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
    greeting = _greeting;
  }
}


이것은 매우 기본적인 스마트 계약입니다. 배포시 Greeting 변수를 설정하고 인사말을 반환하기 위해 호출 할 수있는 함수 (greet)를 노출합니다.


또한 사용자가 인사말 (setGreeting)을 업데이트 할 수 있는 기능을 제공합니다. 이더 리움 블록 체인에 배포되면 사용자가 상호 작용할 수 있는 이러한 방법을 사용할 수 있습니다.


스마트 계약을 약간 수정 해 보겠습니다. hardhat.config.js에서 컴파일러의 solidity 버전을 0.8.3으로 설정 했으므로 동일한 버전의 solidity를 사용하도록 계약도 업데이트해야 합니다.


// contracts/Greeter.sol
pragma solidity ^0.8.3;


이더 리움 블록 체인 읽기 및 쓰기 


스마트 계약과 상호 작용하는 방법에는 읽기 또는 쓰기 / 거래의 두 가지 유형이 있습니다. 계약에서 greet는 읽기로 간주 될 수 있으며 setGreeting은 쓰기 / 트랜잭션으로 간주 될 수 있습니다.


트랜잭션을 작성하거나 초기화 할 때 블록 체인에 쓰여질 트랜잭션에 대해 지불해야 합니다. 이 작업을 수행하려면 거래를 성공적으로 수행하고 이더 리움 블록 체인에서 계약을 실행하는 데 필요한 수수료 또는 가격 인 gas를 지불해야 합니다.


블록 체인에서 읽기만 하고 아무것도 변경하거나 업데이트하지 않는 한 거래를 수행 할 필요가 없으며 그렇게 하는 데 가스나 비용이 들지 않습니다. 호출하는 함수는 연결된 노드에서만 수행되므로 가스를 지불 할 필요가 없으며 읽기가 무료입니다.


React 앱에서 스마트 계약과 상호 작용하는 방식은 ethers.js 라이브러리, 계약 주소 및 hardhat이 계약에서 생성 할 ABI의 조합을 사용하는 것입니다.


ABI 란 무엇입니까? ABI는 애플리케이션 바이너리 인터페이스를 의미합니다. 클라이언트 측 애플리케이션과 상호 작용할 스마트 계약이 배포되는 이더리움 블록 체인 간의 인터페이스라고 생각할 수 있습니다.


ABI는 일반적으로 HardHat과 같은 개발 프레임 워크에 의해 Solidity 스마트 계약에서 컴파일됩니다. Etherscan에서 스마트 계약에 대한 ABI를 자주 찾을 수도 있습니다.


ABI 컴파일 


이제 기본 스마트 계약을 살펴보고 ABI가 무엇인지 알았으므로 프로젝트를 위한 ABI를 컴파일 해 보겠습니다.


이렇게 하려면 명령 줄로 이동하여 다음 명령을 실행합니다.


npx hardhat compile


이제 src 디렉토리에 artifacts라는 새 폴더가 표시됩니다. artifacts/contracts/Greeter.json 파일에는 속성 중 하나로 ABI가 포함되어 있습니다. ABI를 사용해야 할 때 JavaScript 파일에서 가져올 수 있습니다.


import Greeter from './artifacts/contracts/Greeter.sol/Greeter.json'


그런 다음 다음과 같이 ABI를 참조 할 수 있습니다.


console.log("Greeter ABI: ", Greeter.abi)


Ethers.js는 사람이 읽을 수 있는 ABI도 활성화하지만 이 자습서에서는 이에 대해 다루지 않을 것입니다. 


로컬 네트워크 / 블록 체인 배포 및 사용 


다음으로 스마트 계약을 로컬 블록 체인에 배포하여 테스트 해 보겠습니다.


로컬 네트워크에 배포하려면 먼저 로컬 테스트 노드를 시작해야 합니다. 이렇게 하려면 CLI를 열고 다음 명령을 실행하십시오.


npx hardhat node


이 명령을 실행하면 주소 및 개인 키 목록이 표시됩니다.


Hardhat node addresses 


이는 스마트 계약을 배포하고 테스트 하는 데 사용할 수 있는 20 개의 테스트 계정 및 주소입니다. 각 계정에는 10,000 개의 가짜 이더가 들어 있습니다. 잠시 후 테스트 계정을 MetaMask로 가져 와서 사용할 수 있는 방법을 배우겠습니다.


다음으로 계약을 테스트 네트워크에 배포해야 합니다. 먼저 scripts/sample-script.js의 이름을 scripts/deploy.js로 업데이트 합니다.


이제 배포 스크립트를 실행하고 로컬 네트워크에 배포하려는 CLI에 플래그를 지정할 수 있습니다.


npx hardhat run scripts/deploy.js --network localhost


이 스크립트가 실행되면 스마트 계약이 로컬 테스트 네트워크에 배포되어야 하며 그런 다음 상호 작용을 시작할 수 있어야 합니다.


계약이 배포 될 때 로컬 네트워크를 시작할 때 생성 된 첫 번째 계정을 사용했습니다. 


CLI의 출력을 보면 다음과 같은 내용을 볼 수 있습니다.


Greeter deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0


이 주소는 스마트 계약과 대화하기 위해 클라이언트 애플리케이션에서 사용할 주소입니다. 이 주소는 클라이언트 애플리케이션에서 연결할 때 사용해야 하므로 사용 가능한 상태로 유지하십시오.


스마트 계약으로 트랜잭션을 보내려면 npx hardhat 노드를 실행할 때 생성 된 계정 중 하나를 사용하여 MetaMask 지갑을 연결해야 합니다. CLI가 로그 아웃 하는 계약 목록에 계정 번호와 개인 키가 모두 표시되어야 합니다.


➜  react-defi-stack git:(main) npx hardhat node
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========
Account #0: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

...


이 계정을 MetaMask로 가져 와서 이용할 수 있는 가짜 Eth를 사용할 수 있습니다. 이렇게 하려면 먼저 MetaMask를 열고 네트워크를 Localhost 8545로 업데이트합니다.

MetaMask Localhost 


다음으로 MetaMask의 계정 메뉴에서 계정 가져 오기를 클릭합니다.


Import account 



CLI에서 로그 아웃 한 개인 키 중 하나를 복사하여 붙여 넣고 가져 오기를 클릭하십시오. 계정을 가져 오면 계정에 Eth가 표시됩니다.


Imported account 


이제 스마트 계약이 배포되고 사용할 준비가 된 계정이 있으므로 React 앱에서 상호 작용을 시작할 수 있습니다.


React 클라이언트 연결 


이 튜토리얼에서는 CSS로 멋진 UI를 구축하는 것에 대해 걱정하지 않을 것입니다. 그리고 그 모든 것을 시작하고 실행하기 위한 핵심 기능에 100 % 집중했습니다. 거기에서 원하는 경우 가져 와서 멋지게 만들 수 있습니다.


이에 대해 React 애플리케이션에서 원하는 두 가지 목표를 검토해 보겠습니다.


  1. 스마트 계약에서 인사말의 현재 값을 가져옵니다.
  2. 사용자가 인사말 값을 업데이트하도록 허용

이러한 것들을 이해하고 어떻게 이것을 달성 할 수 있습니까? 이를 위해 우리가 해야 할 일은 다음과 같습니다.


  1. 입력 값을 관리하기 위해 입력 필드와 일부 로컬 상태를 만듭니다 (인사말 업데이트).
  2. 애플리케이션이 트랜잭션에 서명하기 위해 사용자의 MetaMask 계정에 연결하도록 허용합니다.
  3. 스마트 계약을 읽고 쓰기 위한 함수 생성

이렇게 하려면 src/App.js를 열고 다음 코드로 업데이트하여 greeterAddress 값을 스마트 계약의 주소로 설정합니다. :


import './App.css';
import { useState } from 'react';
import { ethers } from 'ethers'
import Greeter from './artifacts/contracts/Greeter.sol/Greeter.json'

// Update with the contract address logged out to the CLI when it was deployed 
const greeterAddress = "your-contract-address"

function App() {
  // store greeting in local state
  const [greeting, setGreetingValue] = useState()

  // request access to the user's MetaMask account
  async function requestAccount() {
    await window.ethereum.request({ method: 'eth_requestAccounts' });
  }

  // call the smart contract, read the current greeting value
  async function fetchGreeting() {
    if (typeof window.ethereum !== 'undefined') {
      const provider = new ethers.providers.Web3Provider(window.ethereum)
      const contract = new ethers.Contract(greeterAddress, Greeter.abi, provider)
      try {
        const data = await contract.greet()
        console.log('data: ', data)
      } catch (err) {
        console.log("Error: ", err)
      }
    }    
  }

  // call the smart contract, send an update
  async function setGreeting() {
    if (!greeting) return
    if (typeof window.ethereum !== 'undefined') {
      await requestAccount()
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner()
      const contract = new ethers.Contract(greeterAddress, Greeter.abi, signer)
      const transaction = await contract.setGreeting(greeting)
      await transaction.wait()
      fetchGreeting()
    }
  }

  return (
  <div className="App">
    <header className="App-header">
      <button onClick={fetchGreeting}>Fetch Greeting</button>
      <button onClick={setGreeting}>Set Greeting</button>
      <input onChange={e => setGreetingValue(e.target.value)} placeholder="Set greeting" />
    </header>
  </div>
  );
}

export default App;


테스트하려면 React 서버를 시작하십시오.


npm start


앱이 로드되면 현재 인사말을 가져 와서 콘솔에 로그 아웃 할 수 있어야 합니다. MetaMask 지갑과 계약을 체결하고 가짜 이더를 사용하여 인사말을 업데이트 할 수도 있습니다.


Setting and getting the greeting value 


라이브 테스트 네트워크 배포 및 사용 


Ropsten, Rinkeby 또는 Kovan과 같은 여러 Ethereum 테스트 네트워크가 있으며 메인 넷에 배포 할 필요 없이 공개적으로 액세스 할 수 있는 계약 버전을 사용할 수 있도록 배포 할 수도 있습니다. 이 튜토리얼에서는 Ropsten 테스트 네트워크에 배포 할 것입니다.


시작하려면 먼저 MetaMask 지갑을 업데이트하여 Ropsten 네트워크에 연결하십시오.


Ropsten network 

다음으로,이 테스트 faucet를 방문하여 이 튜토리얼의 나머지 부분에서 사용할 테스트 Ether를 자신에게 보냅니다.


Infura 또는 Alchemy와 같은 서비스에 가입하여 Ropsten (또는 다른 테스트 네트워크 중 하나)에 액세스 할 수 있습니다 (이 자습서에서는 Infura를 사용하고 있습니다).


Infura 또는 Alchemy에서 앱을 생성하면 다음과 같은 엔드 포인트가 제공됩니다.


https://ropsten.infura.io/v3/your-project-id


Infura 또는 Alchemy 앱 구성에서 ALLOWLIST ETHEREUM ADDRESSES를 설정하여 배포 할 계정의 지갑 주소를 포함해야 합니다.


테스트 네트워크에 배포하려면 몇 가지 추가 네트워크 정보로 hardhat 구성을 업데이트 해야 합니다. 설정해야 할 것 중 하나는 배포 할 지갑의 개인 키입니다.


개인 키를 얻으려면 MetaMask에서 내보낼 수 있습니다.


Export private key 


앱에서 이 값을 하드 코딩하지 않고 대신 환경 변수와 같은 것으로 설정하는 것이 좋습니다.


다음으로 다음 구성으로 네트워크 속성을 추가합니다.


module.exports = {
  defaultNetwork: "hardhat",
  paths: {
    artifacts: './src/artifacts',
  },
  networks: {
    hardhat: {},
    ropsten: {
      url: "https://ropsten.infura.io/v3/your-project-id",
      accounts: [`0x${your-private-key}`]
    }
  },
  solidity: "0.7.3",
};

배포하려면 다음 스크립트를 실행하세요.


npx hardhat run scripts/deploy.js --network ropsten



계약이 배포되면 계약과 상호 작용을 시작할 수 있습니다. 이제 Etherscan Ropsten Testnet Explorer에서 라이브 계약을 볼 수 있습니다.


발행 토큰 


스마트 계약의 가장 일반적인 사용 사례 중 하나는 토큰을 만드는 것입니다. 어떻게 할 수 있는지 살펴 보겠습니다. 이 모든 것이 어떻게 작동하는지에 대해 조금 더 알고 있으므로 조금 더 빠르게 진행할 것입니다.


기본 계약 디렉토리에서 Token.sol이라는 새 파일을 작성하십시오.


다음으로 다음 스마트 계약으로 Token.sol을 업데이트합니다.


//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.3;

import "hardhat/console.sol";

contract Token {
  string public name = "Nader Dabit Token";
  string public symbol = "NDT";
  uint public totalSupply = 1000000;
  address public owner;
  mapping(address => uint) balances;

  constructor() {
    balances[msg.sender] = totalSupply;
    owner = msg.sender;
  }

  function transfer(address to, uint amount) external {
    require(balances[msg.sender] >= amount, "Not enough tokens");
    balances[msg.sender] -= amount;
    balances[to] += amount;
  }

  function balanceOf(address account) external view returns (uint) {
    return balances[account];
  }
}


이 토큰 계약은 데모 목적으로 만 사용되며 ERC20을 준수하지 않습니다. ERC20 토큰의 예는 이 계약을 확인하세요. 


이 계약은 "Nader Dabit Token"이라는 새 토큰을 생성하고 공급을 1000000으로 설정합니다.


다음으로 이 계약을 컴파일하십시오.


npx hardhat compile



이제 scripts/deploy.js에서 배포 스크립트를 업데이트하여 이 새 토큰 계약을 포함합니다.


const hre = require("hardhat");

async function main() {
  const [deployer] = await hre.ethers.getSigners();

  console.log(
    "Deploying contracts with the account:",
    deployer.address
  );

  const Greeter = await hre.ethers.getContractFactory("Greeter");
  const greeter = await Greeter.deploy("Hello, World!");

  const Token = await hre.ethers.getContractFactory("Token");
  const token = await Token.deploy();

  await greeter.deployed();
  await token.deployed();

  console.log("Greeter deployed to:", greeter.address);
  console.log("Token deployed to:", token.address);
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });


이제 이 새 계약을 로컬 또는 Ropsten 네트워크에 배포 할 수 있습니다.


npx run scripts/deploy.js --network localhost


계약이 배포되면 이러한 토큰을 다른 주소로 보낼 수 있습니다.


이렇게 하려면 이 작업을 수행하는 데 필요한 클라이언트 코드를 업데이트하겠습니다.


import './App.css';
import { useState } from 'react';
import { ethers } from 'ethers'
import Greeter from './artifacts/contracts/Greeter.sol/Greeter.json'
import Token from './artifacts/contracts/Token.sol/Token.json'

const greeterAddress = "your-contract-address"
const tokenAddress = "your-contract-address"

function App() {
  const [greeting, setGreetingValue] = useState()
  const [userAccount, setUserAccount] = useState()
  const [amount, setAmount] = useState()

  async function requestAccount() {
    await window.ethereum.request({ method: 'eth_requestAccounts' });
  }

  async function fetchGreeting() {
    if (typeof window.ethereum !== 'undefined') {
      const provider = new ethers.providers.Web3Provider(window.ethereum)
      console.log({ provider })
      const contract = new ethers.Contract(greeterAddress, Greeter.abi, provider)
      try {
        const data = await contract.greet()
        console.log('data: ', data)
      } catch (err) {
        console.log("Error: ", err)
      }
    }    
  }

  async function getBalance() {
    if (typeof window.ethereum !== 'undefined') {
      const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' })
      console.log({ account })
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner()
      const contract = new ethers.Contract(tokenAddress, Token.abi, signer)
      contract.balanceOf(account).then(data => {
        console.log("data: ", data.toString())
      })
    }
  }

  async function setGreeting() {
    if (!greeting) return
    if (typeof window.ethereum !== 'undefined') {
      await requestAccount()
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      console.log({ provider })
      const signer = provider.getSigner()
      const contract = new ethers.Contract(greeterAddress, Greeter.abi, signer)
      const transaction = await contract.setGreeting(greeting)
      await transaction.wait()
      fetchGreeting()
    }
  }

  async function sendCoins() {
    if (typeof window.ethereum !== 'undefined') {
      await requestAccount()
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner()
      const contract = new ethers.Contract(tokenAddress, Token.abi, signer)
      contract.transfer(userAccount, amount).then(data => console.log({ data }))
    }
  }

  return (
  <div className="App">
    <header className="App-header">
      <button onClick={fetchGreeting}>Fetch Greeting</button>
      <button onClick={setGreeting}>Set Greeting</button>
      <input onChange={e => setGreetingValue(e.target.value)} placeholder="Set greeting" />

      <br />
      <button onClick={getBalance}>Get Balance</button>
      <button onClick={sendCoins}>Send Coins</button>
      <input onChange={e => setUserAccount(e.target.value)} placeholder="Account ID" />
      <input onChange={e => setAmount(e.target.value)} placeholder="Amount" />
    </header>
  </div>
  );
}

export default App;


다음으로 앱을 실행합니다.


npm start


Get Balance를 클릭하고 콘솔에 로그 아웃 한 계정에 1,000,000 개의 코인이 있음을 확인할 수 있습니다.


토큰 추가를 클릭하여 MetaMask에서 볼 수도 있습니다.


Add token 


다음으로 사용자 지정 토큰을 클릭하고 토큰 계약 주소를 입력 한 다음 토큰 추가를 클릭합니다. 이제 지갑에서 토큰을 사용할 수 있습니다.


NDT 



다음으로, 그 동전을 다른 주소로 보내 봅시다.


이렇게 하려면 다른 계정의 주소를 복사하고 업데이트 된 React UI를 사용하여 해당 주소로 보냅니다. 토큰 금액을 확인할 때 원래 금액에서 주소로 보낸 금액을 뺀 금액과 같아야 합니다.


결론 


좋아요, 우리는 여기에서 많이 다루었지만 저에게 이것은 일종의 빵과 버터 /이 스택을 시작하는 핵심이며 이 모든 것을 배우는 사람으로서 뿐만 아니라 미래에 필요한 것을 참조해야 하는 경우 미래. 많은 것을 배웠기를 바랍니다.


MetaMask 외에도 여러 지갑을 지원하려면 Web3 Modal을 확인하십시오. 이것은 매우 간단하고 사용자 정의 가능한 구성으로 앱에서 여러 공급자에 대한 지원을 쉽게 구현할 수 있도록 합니다.


앞으로의 튜토리얼과 가이드에서는 더 복잡한 스마트 계약 개발과 이를 하위 그래프로 배포하여 그 위에 GraphQL API를 노출하고 페이지 매김 및 전체 텍스트 검색과 같은 것을 구현하는 방법에 대해 알아볼 것입니다.


또한 IPFS 및 Web3 데이터베이스와 같은 기술을 사용하여 분산 된 방식으로 데이터를 저장하는 방법에 대해서도 알아볼 것입니다.


https://dev.to/dabit3/the-complete-guide-to-full-stack-ethereum-development-3j13