반응형

사용할 프레임 워크 : chakra UI

강의에서는 리액트 17 버전을 사용중이기 때문에 차크라 UI 는 1버전으로 다운로드 해줘야 정상 실행된다.

 

 

App.tsx

import React, { FC, useState, useEffect  } from "react";

import { BrowserRouter, Routes, Route} from "react-router-dom"; 
import Layout from "./components/Layout";
import Main from "./routes/main";
import MyAnimal from "./routes/my-animal";
import SaleAnimal from "./routes/sale-animal";

const App: FC = () => {
  const [account, setAccount] = useState<string>("");

  const getAccount = async () => {
    try{
      if(window.ethereum){
        const accounts = await window.ethereum.request({
          method:'eth_requestAccounts',
        })
        setAccount(accounts[0])
      } else{
        alert("Install Metamask");
      }
    }catch(err){
      console.log(err);
    }
  }

  useEffect(() => {
    getAccount();
    console.log(account)
  }, [account])
  

  return (
    <BrowserRouter>
      <Layout>
        <Routes>
          <Route path="/" element={<Main account={account}/>} />
          <Route path="my-animal" element={<MyAnimal account={account}/>} />
          <Route path="sale-animal" element={<SaleAnimal account={account}/>} />
        </Routes>
      </Layout>
      
    </BrowserRouter>
  )
};

export default App;
  • account 가 바뀔때마다 렌더링을 해주는 방식
  • Metamask 설치 유무또한 확인해 주어야한다.

main.tsx

import React , {FC, useState} from 'react';
import {Button, Box, Text, Flex} from '@chakra-ui/react';
import { mintAnimalTokenContract } from '../contracts';
import AnimalCard from '../components/AnimalCard';

interface MainProps {
    account :string;
}

const Main : FC<MainProps> = ({account}) => {
    const [NewAnimalType, setNewAnimalType] = useState<string>("")

    const onClickMint = async () =>{
        try{
            if(!account) return;

            const response = await mintAnimalTokenContract.methods
                                    .mintAnimalToken()
                                    .send({from: account});

            console.log(response);
            if(response.status){
                const balanceLength = await mintAnimalTokenContract.methods
                                    .balanceOf(account)
                                    .call();

                const animalTokenId = await mintAnimalTokenContract.methods
                .tokenOfOwnerByIndex(account, parseInt(balanceLength.length, 10) -1)
                .call();

                const animalType = await mintAnimalTokenContract.methods
                .animalTypes(animalTokenId)
                .call();

                setNewAnimalType(animalType);
                console.log(NewAnimalType);
            }
        }catch(err){
            console.log(err);
        }
    }

    return <Flex w="full" h= "100vh" justifyContent="center" alignItems="center" direction="column">
        <Box>
            {NewAnimalType ? <AnimalCard animalType={NewAnimalType} /> : <Text>Let's mint Animal Card!</Text>}
        </Box>
        <Box>
            <Button mt={4} size="sm" colorScheme="blue" onClick={onClickMint}>Mint</Button>
        </Box>
    </Flex>
}
export default Main;

함수설명

  1. onClickMint
    • 컨트랙트 함수에서 mint 를 호출하여 토큰을 생성하고 토큰의 id, type 을 저장한다.

my-animal.tsx

import React , {FC, useState, useEffect} from "react";
import AnimalCard from "../components/AnimalCard";
import { mintAnimalTokenContract, saleAnimalTokenAddress, saleAnimalTokenContract } from "../contracts";
import {Grid, Box , Button, Text, Flex} from '@chakra-ui/react'
import MyAnimalCard, { IMyanimalCard } from "../components/MyAnimalCard";
interface MyAnimalProps {
    account : string;
}

const MyAnimal : FC<MyAnimalProps> = ({account}) =>{
    const [AnimalCardArray, setAnimalCardArray] = useState<IMyanimalCard[]>();
    const [SaleStatus, setSaleStatus] = useState<boolean>(false);

    const getAnimalTokens = async() => {
        try{
            
            const balanceLength = await mintAnimalTokenContract.methods
            .balanceOf(account)
            .call();

            if(balanceLength =="0") return ;

            const tempAnimalCardArray: IMyanimalCard[] = [];

            const response = await mintAnimalTokenContract.methods.getAnimalTokens(account).call();
            console.log(response)

            response.map((v: IMyanimalCard) => {
                tempAnimalCardArray.push({animalTokenId : v.animalTokenId, animalType : v.animalType, animalPrice : v.animalPrice})
            })
            
            setAnimalCardArray(tempAnimalCardArray);
        }catch(err){
            console.log(err);
        }
    };

    const getIsApprovedForAll = async () =>{
        try{
            const response = await mintAnimalTokenContract.methods
            .isApprovedForAll(account, saleAnimalTokenAddress)
            .call();
            if(response){
                setSaleStatus(response);
            }
            console.log(response);
        }catch(err){
            console.log(err)
        }
    }

    const onClickApproveToggle = async () =>{
        try{
            if(!account) return;

            const response = await mintAnimalTokenContract.methods
            .setApprovalForAll(saleAnimalTokenAddress, !SaleStatus)
            .send({from:account});

            if(response.status){
                setSaleStatus(!SaleStatus);
            }
        }catch(err){
            console.log(err);
        }
    }

    useEffect(() => {
        if(!account) return;
        getAnimalTokens();
        getIsApprovedForAll();
    }, [account])
    
    useEffect(() => {
        console.log(AnimalCardArray)
    }, [AnimalCardArray])

    return(
        <>
        <Flex alignItems="center">
            <Text display="inline-block">
                Sale Status : {SaleStatus ? "True" : "False"}
            </Text>
            <Button size="xs" ml={2} colorScheme={SaleStatus ? "red" : "blue"} onClick={onClickApproveToggle}>
                {SaleStatus ? "Cancel" : "Approve"}
            </Button>
        </Flex>
        <Grid templateColumns ="repeat(4,1fr)" gap={8} mt={2}>
            {
                AnimalCardArray && AnimalCardArray.map((v,i) =>{
                    return <MyAnimalCard key={i} animalTokenId={v.animalTokenId} animalType={v.animalType} animalPrice={v.animalPrice} saleStatus={SaleStatus} account={account}/>;
                }
            )}
        </Grid>
        </>
    );
}

export default MyAnimal

함수 설명

  1. getAnimalTokens
    • account 가 소유한 카드들을 불러와서 배열형태로 리턴한다.
  2. getIsApprovedAll
    • account 와 카드의 주인이 지닌 주소를 비교해 같지 않은지 확인 한다.
    • true 값이 반환되어야 판매등록이 가능하다.
  3. onClickApproveToggle
    • getIsApprovedAll 을 위한 버튼 함수

sale-animal.tsx

import React , {FC, useState, useEffect} from "react";
import {Grid} from "@chakra-ui/react"
import { IMyanimalCard } from "../components/MyAnimalCard";
import { mintAnimalTokenContract, saleAnimalTokenContract } from "../contracts";
import SaleAnimalCard from "../components/SaleAnimalCard";


interface SaleAnimalProps {
    account:string;
}

const SaleAnimal:FC<SaleAnimalProps> = ({account}) =>{
    const [saleAnimalCard, setSaleAnimalCard] = useState<IMyanimalCard[]>()

    const getOnSaleAnimalTokens = async () =>{
        try{
            const getOnSaleAnimalTokenArrayLength = await saleAnimalTokenContract.methods.getOnSaleAnimalTokenArrayLength().call();

            const tempOnSaleArray : IMyanimalCard[] = [];

            for(let i = 0;i<parseInt(getOnSaleAnimalTokenArrayLength, 10); i++){
                const animalTokenId = await saleAnimalTokenContract.methods.onSaleAnimalTokenArray(i).call();

                const animalType = await mintAnimalTokenContract.methods.animalTypes(animalTokenId).call();

                const animalPrice = await saleAnimalTokenContract.methods.animalTokenPrices(animalTokenId).call();

                tempOnSaleArray.push({animalTokenId, animalType, animalPrice})
            }

            setSaleAnimalCard(tempOnSaleArray);
        } catch(err){
            console.log(err);
        }
    }

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

    return(
        <Grid mt={4} templateColumns="repeat(4, 1fr)" gap={8}>
                {saleAnimalCard && saleAnimalCard.map((v,i) => {
                    return <SaleAnimalCard key={i} animalType={v.animalType} animalPrice={v.animalPrice} animalTokenId={v.animalTokenId} account={account} getOnSaleAnimalTokens={getOnSaleAnimalTokens}/>
                })}
        </Grid>
    );
}

export default SaleAnimal

함수 설명

  1. getOnSaleAnimalTokens
    • 판매중인 토큰들을 불러오는 함수
    • my-animal 과 같이 컨트랙트에서 배열을 받아오면 되지만 강의에서는 귀찮아서 for문으로 했다.
반응형

'BlockChain > Solidity' 카테고리의 다른 글

[DP/NFT] 3. SaleAnimalToken 작성  (0) 2022.09.05
[DP/NFT] 2. Minting contract 작성  (0) 2022.09.05
[DP/NFT] 1. Install Solidity & Metamask  (0) 2022.08.25

+ Recent posts