D Web3 시리즈

3D 웹3 시리즈
3D-web3 시리즈의 마지막 포스팅입니다.

1 -
2 -
삼 -
4 - 3D 웹 - react-web3

안정적인 버전은 v6이며 현재 v8은 베타 버전입니다.
Uniswap 엔지니어링 책임자인 Noah Zinsmeister가 개발했습니다.

높은 수준에서 web3-react는 dApp과 관련된 특정 주요 데이터(예: 사용자의 현재 계정)가 최신 상태로 유지되도록 하는 상태 머신입니다. 이를 위해 web3-react는 Context를 사용하여 이 데이터를 효율적으로 저장하고 애플리케이션에서 필요할 때마다 주입합니다.

Web3-react v6은 컨텍스트를 사용하여 이 데이터를 효율적으로 저장하고 애플리케이션에서 필요할 때마다 주입합니다.

유용한 링크:
Uniswap/web3-reactsource code
Uniswap/web3-reactdocuments
How to Use Web3React in Your Next Project

다음과 같이 web3 환경을 만드는 다른 여러 라이브러리가 있습니다.
Rainbowkit || Wagmi || Scaffold-eth || useDApp || web3modal || Web3-UI

우리는 배후에서 Ethers.js를 사용하고 있습니다. DApp에서 컨텍스트를 유지하고 블록체인과 쉽게 연결하기 위해. 또한 다양한 종류의 블록체인 공급자, 지갑 또는 체인을 연결하고 블록체인을 보다 효율적으로 쿼리합니다.

어쨌든 이더를 사용하여 전체 DApp을 직접 구축할 수 있습니다.

우리는 다음을 사용할 것입니다:

사용자 측의 MaskMask 지갑
이더리움 웹 클라이언트 라이브러리 - "@web3-react", "ethersproject"
  • "@web3-react"에서: Web3ReactProvider, 컨텍스트, useWeb3React, 후크, InjectedConnector. 지갑을 연결하고 블록체인에서 데이터를 가져옵니다. ("ethers"위에 내장됨)
  • "@ethersproject"에서: 계약, Web3Provider. 트랜잭션을 블록체인으로 전송합니다.
  • 제공자로부터 브로드캐스트된 이벤트를 수신하기 위해 "이벤트"라이브러리를 사용하고 있습니다.

  • npm i @web3-react/core @web3-react/injected-connector
    npm i @ethersproject/contracts @ethersproject/providers
    npm i events
    

    이 데모에서는 동일한 토큰을 BSC 테스트넷과 뭄바이(폴리곤 테스트넷)에 배포하고 있습니다.

    먼저 브라우저에 메타마스크, TrustWallet(WalletConnect) 또는 코인베이스 확장 프로그램이 설치되어 있어야 합니다.

    @Notice WalletConnect establishes an encrypted connection between your wallet and the DApp. Use it with Ex. with "Trust Wallet"

    You can check all css details in . This article focuses on the web3 connection.



    1단계_ 모든 구성 요소 자식에 대한 web3 컨텍스트 생성

    컨텍스트 제공자 추가

    web3-react relies on the existence of a Web3ReactProvider at the root of your application.



    저수준 공급자에서 web3 편의 라이브러리 개체를 인스턴스화하는 단일 getLibrary 소품이 필요합니다.

    
    import React, { useEffect } from 'react';
    import { Web3ReactProvider } from '@web3-react/core'
    import { Web3Provider } from '@ethersproject/providers'
    
    function getLibrary(provider) {
        const library = new Web3Provider(provider)
        library.pollingInterval = 12000
        return library
    }
    
    function Web3ContextProvider({ children }) {
    
        return (
            <Web3ReactProvider getLibrary={getLibrary}>
                {children}
            </Web3ReactProvider>
        )
    }
    
    export default Web3ContextProvider
    


    App.jsx에 추가

    import Web3ContextProvider from './web3/Web3ContextProvider';
    import ConnectWallet from './web3/ConnectWallet';
    
    return (
    ...
    <Web3ContextProvider style={{ height: '15vh' }} className='header'>
                        <ConnectWallet />
    </Web3ContextProvider>
    ...
    )
    


    2단계_ Web3 개체 인스턴스화 및 메서드 정의

    ConnectWallet.jsx 생성
    "useWeb3React"를 사용하여 "InjectedConnector"를 사용하여 블록체인에 연결합니다.
    MetaMask를 사용하면 "windows.ethereum"을 통해 공급자에게 연락할 수 있습니다.

    지갑을 연결하고 연결 해제하는 버튼과 현재 체인을 변경할 수 있는 버튼을 제공합니다.

    import { useEffect } from 'react'
    import { useWeb3React } from '@web3-react/core'
    import { InjectedConnector } from '@web3-react/injected-connector'
    import "../App.css"
    import "../Button.css"
    import "../Select.css"
    import { changeChainById } from "./transaction/chains"
    import ClaimToken from "./ClaimToken"
    
    const ConnectWallet = () => {
    
        const injectedConnector = new InjectedConnector({
            supportedChainIds: [1, 97, 80001],
        })
    
        const { chainId, account, activate, active, library, deactivate, connector } = useWeb3React()
    
        const activateWallet = () => {
            activate(injectedConnector)
        }
        const deactivateWallet = () => {
            deactivate(injectedConnector)
        }
        const changeChain = (_chainID) => {
            changeChainById(_chainID)
        }
    
        useEffect(() => {
            if (!chainId) return
            document.getElementById('select-form').value = chainId
        }, [chainId])
    
        return (
            <main className="web3-navbar">
                <h2 >Welcome to 3D web3 series</h2>
                <div className='connect-box'>
                    <b>ChainId: {chainId}</b>
                    <div>Account: {account}</div>
                    {active ? (
                        <button type="button" className='button-4' onClick={deactivateWallet}>
                            Disconnect
                        </button>
                    ) : (
                        <button type="button" className='button-3' onClick={activateWallet}>
                            Connect Wallet
                        </button>
                    )}
                </div>
                <div className='box'>
                    <select id='select-form' onChange={e => {
                        let _chainID = e.target.value
                        changeChain(_chainID)
                    }}>
                        <option key={1} value={1}>Ethereum Chain</option>
                        <option key={97} value={97}>BSC testnet</option>
                        <option key={80001} value={80001}>Mumbai testnet</option>
                    </select>
                </div>
                <div>
                    <ClaimToken
                        account={account}
                        chainId={chainId}
                    />
                </div>
            </main>
        )
    }
    
    export default ConnectWallet
    


    3단계_ 체인간 추가 및 변경 방법.

    우리가 사용하고 있는 다른 블록체인metamask RPC API's 사이를 전환하기 위해 메소드를 구축했습니다.

    "wallet_switchEthereumChain"메서드를 호출하여 사용자에게 체인 변경을 요청합니다.

    사용자가 특정 체인을 구성하지 않은 경우 "wallet_addEthereumChain"메서드를 잡아서 호출하여 사용자에게 선택한 체인을 추가하도록 요청합니다.

    @알아채다. tryCatch를 사용하십시오. 이를 처리하기 위해 일부 반복되는 오류가 있습니다here.

    체인을 변경/추가하기 위해서는 체인 정보를 제공해야 합니다. 코드를 체크인하십시오.

    export const changeChainById = async (chainID) => {
      if (!window.ethereum)
        return alert("install metamask extension in your browser");
      try {
        await ethereum.request({
          method: "wallet_switchEthereumChain",
          params: [{ chainId: chains[chainID].chainId }],
        });
      } catch (switchError) {
        // This error code indicates that the chain has not been added to MetaMask.
        if (switchError.code === 4902) {
          try {
            await ethereum.request({
              method: "wallet_addEthereumChain",
              params: [chains[chainID]],
            });
          } catch (addError) {
            console.log("error: ", addError);
            if (ex.code === 32002)
              return alert("already pending request from user in metamask");
            else
              return alert(
                "Disconnect wallet from metamask configuration and try again!"
              );
          }
        }
        // handle other "switch" errors
      }
      return;
    };
    
    const ETH = {
      name: "Ether",
      symbol: "ETH",
      decimals: 18,
    };
    const MATIC = {
      name: "Matic",
      symbol: "MATIC",
      decimals: 18,
    };
    const BNB = {
      name: "Binance",
      symbol: "BNB",
      decimals: 18,
    };
    
    const chains = {
      1: {
        chainId: "0x1",
        chainName: "Ethereum mainnet",
        nativeCurrency: ETH,
        rpcUrls: [
          import.meta.env.VITE_APP_INFURA_KEY
            ? `https://mainnet.infura.io/v3/${import.meta.env.VITE_APP_INFURA_KEY}`
            : undefined,
          import.meta.env.VITE_APP_ALCHEMY_KEY
            ? `https://eth-mainnet.alchemyapi.io/v2/${
                import.meta.env.VITE_APP_ALCHEMY_KEY
              }`
            : undefined,
          "https://cloudflare-eth.com",
        ].filter((url) => url !== undefined),
        blockExplorerUrls: ["https://etherscan.com/"],
      },
      97: {
        chainId: "0x61",
        chainName: "Binance Testnet",
        nativeCurrency: BNB,
        rpcUrls: [
          "https://data-seed-prebsc-1-s1.binance.org:8545/",
          "https://data-seed-prebsc-2-s1.binance.org:8545/",
          "http://data-seed-prebsc-1-s2.binance.org:8545/",
          "https://data-seed-prebsc-2-s3.binance.org:8545/",
        ],
        // rpcUrls: 'https://data-seed-prebsc-1-s1.binance.org:8545',
        blockExplorerUrls: ["https://testnet.bscscan.com/"],
      },
      80001: {
        chainId: "0x13881",
        chainName: "Polygon Mumbai",
        nativeCurrency: MATIC,
        rpcUrls: [
          import.meta.env.VITE_APP_INFURA_KEY
            ? `https://polygon-mumbai.infura.io/v3/${
                import.meta.env.VITE_APP_INFURA_KEY
              }`
            : undefined,
        ].filter((url) => url !== undefined),
        blockExplorerUrls: ["https://mumbai.polygonscan.com/"],
      },
    };
    
    


    4단계_ 트랜잭션 정의

    UI를 정의하는 ClaimToken.jsx 구성 요소를 만듭니다.

    
    import { burnToken, claimToken } from './transaction/transaction'
    import "../App.css"
    
    export default function TransactionMetaMask(props) {
    
        const claimTokenTx = () => {
            if (props.chainId === 97 || props.chainId === 80001) {
                claimToken(props.provider, props.account, props.chainId, 1)
            } else {
                scrollTo(0, 0)
                alert('Tokens are only available in BSC and Polygon testnets')
            }
        }
        const burnTokenTx = () => {
            if (props.chainId === 97 || props.chainId === 80001) {
                burnToken(props.provider, props.account, props.chainId, 1)
            } else {
                scrollTo(0, 0)
                alert('Tokens are only available in BSC and Polygon testnets')
            }
        }
    
        return (
            <div className='token-buttons'>
                <button type="button" className='button-3' onClick={claimTokenTx}>
                    Claim Token
                </button>
                <button type="button" className='button-3' onClick={burnTokenTx}>
                    Burn Token
                </button>
            </div>
        )
    }
    
    


    트랜잭션을 보내 블록체인 데이터를 수정하려면 "@ethersproject/providers"에서 직접 공급자를 가져와서 "서명자"개체를 생성할 수 있습니다.

    이제 스마트 계약 주소, ABI 및 서명자를 사용하여 "계약"개체를 만듭니다(계약 메서드와 상호 작용할 준비가 됨).

    import { Contract } from "@ethersproject/contracts";
    import { Web3Provider } from "@ethersproject/providers";
    
    // Same ABI for all SC living in EVM compatible networks
    export const contractAbi = [...];
    
    const contractsAddress = {
      80001: "0x41e6913ce749018910e45980996dac1f99012c96", // MUMBAI
      97: "0x6ec4c5ce6cc67729d89785f715e103e5981c9780", // BSC Test
    };
    // TODO
    export const getContract = (chainId) => {
      // using ethersproject to set signer using default provider
      const provider = new Web3Provider(window.ethereum);
      const signer = provider.getSigner();
    
      const contractAddress = contractsAddress[chainId];
    
      const contract = new Contract(contractAddress, contractAbi, signer);
      return contract;
    };
    
    


    마지막으로 비동기 RPC를 보내고 모든 오류를 포착합니다.

    import { getContract } from "./contract";
    
    // writeToContractUsingWeb3React
    const claimToken = async (account, chainId, amount) => {
      try {
        const myContract = getContract(chainId);
        // Metamask calculates gas, but, for walletConnect and coinbase we need to set gas limit
        const overrides = {
          gasLimit: 230000,
        };
        const txResponse = await myContract.mint(account, amount, overrides);
        const txReceipt = await txResponse.wait();
        console.log(txReceipt);
        // alert(txReceipt);
      } catch (ex) {
        console.log(ex);
        if (ex.code === 32002)
          return alert("already pending request from user in metamask");
    
        if (ex.code === 4001) return alert("User denied transaction signature");
        return alert('"Connect / Disconnect" your wallet and try again.');
      }
    };
    
    const burnToken = async (chainId, amount) => {
      try {
        const myContract = getContract(chainId);
        // Metamask calculates gas, but, for walletConnect and coinbase we need to set gas limit
        const overrides = {
          gasLimit: 230000,
        };
        const txResponse = await myContract.burn(amount, overrides);
        const txReceipt = await txResponse.wait();
        console.log(txReceipt);
        // alert(txReceipt);
      } catch (ex) {
        console.log(ex);
        if (ex.code === 32002)
          return alert("already pending request from user in metamask");
        if (ex.code === 4001) return alert("User denied transaction signature");
        return alert('"Connect / Disconnect" your wallet and try again.');
      }
    };
    
    export { claimToken, burnToken };
    
    


    이제 저장소를 포크하고 localy를 시도하십시오.

    npm install
    
    // add to .env.local
    // VITE_APP_INFURA_KEY
    // VITE_APP_ALCHEMY_KEY
    
    npm run dev
    


    또는 Code Sand Box 데모를 확인하십시오.
    RPC 공급자는 수시로 비활성화될 수 있습니다.
    이 경우 infura 또는 alchemy에서 자체 무료 RPC API를 제공하십시오.

    BSC 테스트넷 토큰 주소, bscscan
    뭄바이 토큰 주소, polygonscan



    도움이 되었기를 바랍니다.

    좋은 웹페이지 즐겨찾기