Skip to content

moriroKim/DAPP_PROJECT

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

15 Commits
Β 
Β 
Β 
Β 

Repository files navigation

DAPP_PROJECT

블둝체인 κ²½λ§€μ‚¬μ΄νŠΈ ν”„λ‘œμ νŠΈ

이 ν”„λ‘œμ νŠΈλŠ” 블둝체인 κ²½λ§€ μ‹œμŠ€ν…œμ„ κ΅¬ν˜„ν•œ μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μž…λ‹ˆλ‹€. μ‚¬μš©μžλŠ” 경맀에 μ°Έμ—¬(μž…μ°°)ν•˜κ³ , μΆœκΈˆν•  수 있으며, κ²½λ§€ μ£Όμ΅œμžλŠ” κ²½λ§€ μƒνƒœλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ‹œμ—°μ˜μƒ 링크

ν”„λ‘œμ νŠΈ μ°Έμ—¬μž

이름 μ—­ν•  GitHub
κΉ€μ§„λͺ¨ FE/BE GitHub
λ°©ν˜„λ―Ό BE GitHub

각 νŒ€μ›μ€ ν”„λ‘œμ νŠΈμ˜ λ°±μ—”λ“œ κ°œλ°œμ— κΈ°μ—¬ν•˜μ˜€μœΌλ©°, GitHub 링크λ₯Ό 톡해 각자의 μž‘μ—…μ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ£Όμš” κΈ°λŠ₯

  • κ²½λ§€ 이벀트 처리: 경맀와 κ΄€λ ¨λœ λ‹€μ–‘ν•œ 이벀트λ₯Ό μ‹€μ‹œκ°„μœΌλ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.
  • μƒνƒœ 관리: zustandλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒνƒœλ₯Ό κ΄€λ¦¬ν•˜λ©°, 각 μ»΄ν¬λ„ŒνŠΈμ˜ μƒνƒœκ°€ μ΅œμ‹ ν™”λ˜λ„λ‘ 보μž₯ν•©λ‹ˆλ‹€.
  • Web3.js μ‚¬μš©: 솔리디티 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜κ³ , μ†ŒμΌ“ 톡신을 톡해 이벀트λ₯Ό κ΅¬λ…ν•©λ‹ˆλ‹€.

μ‚¬μš© μŠ€νƒ

  • ν”„λ‘ νŠΈμ—”λ“œ: React, TypeScript, Web3.js, Ganache
  • λ°±μ—”λ“œ: Solidity(Remix)
  • μƒνƒœ 관리: Zustand
  • μŠ€νƒ€μΌλ§: CSS, Tailwind CSS
  • 기타: lodash

νŠΈλŸ¬λΈ”μŠˆνŒ… κ²½ν—˜

1. 비동기 ν•¨μˆ˜

비동기 ν•¨μˆ˜μ˜ 동기화 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ lodash의 debounceλ₯Ό μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” λ²„νŠΌμ„ λΉ λ₯΄κ²Œ ν΄λ¦­ν–ˆμ„ λ•Œ 비동기 ν•¨μˆ˜μ™€μ˜ 싱크λ₯Ό μ‘°μ ˆν•˜λŠ” 데 μœ μš©ν–ˆμŠ΅λ‹ˆλ‹€.

import { debounce } from "lodash";

const handlePlaceBid = debounce(async () => {
  if (amount > balance) {
    addLog(`[μ—λŸ¬]: μ§€κ°‘μ˜ μž”κΈˆμ΄ λΆ€μ‘±ν•©λ‹ˆλ‹€!`);
  } else {
    try {
      await placeBid(amount, currentWallet);
    } catch (e: unknown) {
      const errorMsg = e instanceof Error ? e.message : String(e);
      const delimeter = errorMsg.split("revert")[1];
      addLog(`[μ—λŸ¬]: ${delimeter}`);
    }
  }
}, 200); // 비동기 ν•¨μˆ˜μ™€ 싱크λ₯Ό λ§žμΆ”κΈ° μœ„ν•΄ 0.2초 λ””λ°”μš΄μŠ€ μΆ”κ°€

2. μƒνƒœ 관리

μƒνƒœκ°€ 각 μ»΄ν¬λ„ŒνŠΈλ³„λ‘œ μ΅œμ‹ ν™”λ˜μ§€ μ•ŠλŠ” 문제λ₯Ό zustand둜 ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€. zustandλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒνƒœλ₯Ό μ€‘μ•™μ—μ„œ κ΄€λ¦¬ν•˜κ³ , 각 μ»΄ν¬λ„ŒνŠΈκ°€ λ™μΌν•œ μƒνƒœλ₯Ό μ°Έμ‘°ν•˜λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€.

  // useUserStore
  walletAddresses: string[]; // μ‚¬μš©μžμ˜ μ§€κ°‘ μ£Όμ†Œ λͺ©λ‘
  currentWallet: string;     // ν˜„μž¬ μ„ νƒλœ μ§€κ°‘ μ£Όμ†Œ
  ownerWallet: string;       // κ²½λ§€ μ†Œμœ μžμ˜ μ§€κ°‘ μ£Όμ†Œ
  bid: number;               // ν˜„μž¬ μž…μ°° κΈˆμ•‘
  balance: number;           // ν˜„μž¬ μ§€κ°‘μ˜ μž”μ•‘

  // λ©”μ„œλ“œ
  getWallets: () => Promise<void>; // μ‚¬μš©μžμ˜ μ§€κ°‘ μ£Όμ†Œλ₯Ό κ°€μ Έμ˜€λŠ” λ©”μ„œλ“œ
  placeBid: (bid: number, currentWallet: string) => Promise<void>; // μž…μ°°μ„ μˆ˜ν–‰ν•˜λŠ” λ©”μ„œλ“œ
  withdraw: (currentWallet: string) => Promise<void>; // μΆœκΈˆμ„ μˆ˜ν–‰ν•˜λŠ” λ©”μ„œλ“œ
  switchWallet: (currentWallet: string) => void; // 지갑을 μ „ν™˜ν•˜λŠ” λ©”μ„œλ“œ
  getBalance: (currentWallet: string) => Promise<void>; // μ§€κ°‘μ˜ μž”μ•‘μ„ κ°€μ Έμ˜€λŠ” λ©”μ„œλ“œ


  // useAuctionStore
  item: AuctionItem; // ν˜„μž¬ κ²½λ§€μ•„μ΄ν…œ 정보
  status: "μ’…λ£Œλ¨" | "진행쀑" | "μ—λŸ¬"; // κ²½λ§€ μƒνƒœ
  highestBid: number; // ν˜„μž¬ 졜고 μž…μ°°κ°€
  highestBidder: string; // ν˜„μž¬ 졜고 μž…μ°°κ°€μ˜ μ£Όμ†Œ
  timeLeft: number; // 남은 μ‹œκ°„ (μ΄ˆλ‹¨μœ„)
  getAuctionItem: () => Promise<void>; // κ²½λ§€ μ•„μ΄ν…œ 정보λ₯Ό κ°€μ Έμ˜€λŠ” λ©”μ„œλ“œ
  updateHighestBid: (amount: number, bidder: string) => void; // 졜고 μž…μ°°κ°€λ₯Ό μ—…λ°μ΄νŠΈν•˜λŠ” λ©”μ„œλ“œ
  getTimeLeft: () => Promise<void>; // 남은 μ‹œκ°„μ„ κ°€μ Έμ˜€λŠ” λ©”μ„œλ“œ
  getStatus: () => Promise<void>; // κ²½λ§€ μƒνƒœλ₯Ό κ°€μ Έμ˜€λŠ” λ©”μ„œλ“œ
  getHighestBid: () => Promise<void>; // 졜고 μž…μ°°κ°€λ₯Ό κ°€μ Έμ˜€λŠ” λ©”μ„œλ“œ
  getHighestBidder: () => Promise<void>; // 졜고 μž…μ°°μžμ˜ μ£Όμ†Œλ₯Ό κ°€μ Έμ˜€λŠ” λ©”μ„œλ“œ
  deactivate: (ownerWallet: string) => Promise<void>; // κ²½λ§€λ₯Ό λΉ„ν™œμ„±ν™”ν•˜λŠ” λ©”μ„œλ“œ
  withdrawFunds: (ownerWallet: string) => Promise<void>; // 남은 μžκΈˆμ„ μΆœκΈˆν•˜λŠ” λ©”μ„œλ“œ

  // useLogStore
  logs: LogItem[]; // 둜그 ν•­λͺ© λͺ©λ‘

  // λ©”μ„œλ“œ
  addLog: (message: string) => void; // 둜그λ₯Ό μΆ”κ°€ν•˜λŠ” λ©”μ„œλ“œ
  clearLogs: () => void; // λͺ¨λ“  둜그λ₯Ό μ§€μš°λŠ” λ©”μ„œλ“œ

3. Web3.js와 μ†ŒμΌ“ 톡신

Web3.jsλ₯Ό 처음 μ‚¬μš©ν•˜λ©΄μ„œ μ†”λ¦¬λ””ν‹°μ˜ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” 방법과 μ†ŒμΌ“μœΌλ‘œ 이벀트λ₯Ό ꡬ독할 수 μžˆλ‹€λŠ” 점을 μ•Œκ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

useAuctionEvents 훅을 λ§Œλ“€μ–΄ 각 μ΄λ²€νŠΈκ°€ λ°œμƒν•  λ•Œλ§ˆλ‹€ λ°›μ•„μ˜€λŠ” 둜그λ₯Ό μ „μ—­ μƒνƒœλ‘œ κ΄€λ¦¬ν•˜κ³  LogBox μ»΄ν¬λ„ŒνŠΈμ— 둜그λ₯Ό 좜λ ₯ν•˜κ²Œλ” ν–ˆμŠ΅λ‹ˆλ‹€.

λ™μ‹œμ—, μ΄λ²€νŠΈμ— 따라 getBalance() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ μ „μ—­ μƒνƒœλ‘œ κ΄€λ¦¬λ˜κ³  μžˆλŠ” balanceλ₯Ό μ„œλ²„μ˜ μ΅œμ‹  데이터와 λ™κΈ°ν™”ν•©λ‹ˆλ‹€.

이λ₯Ό 톡해 μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€μ™€ μ„œλ²„ κ°„μ˜ 데이터 일관성을 μœ μ§€ν•©λ‹ˆλ‹€.

// auctionInstance.ts
import Web3 from "web3";

const web3 = new Web3(new Web3.providers.WebsocketProvider("ws://localhost:7545"));
const auctionContract = new web3.eth.Contract(AuctionABI as AbiItem[], CONTRACT_ADDRESS);
// useAuctionEvents
import { useEffect } from "react";
import { useAuctionStore } from "./stores/useAuctionStore";
import { useUserStore } from "./stores/useUserStore";
import { auctionContract } from "./auctionInstance";
import { EventData } from "web3-eth-contract";
import { useLogStore } from "./stores/useLogStore";
import { formatTime, weiToEther } from "./utils";

const useAuctionEvents = () => {
  const updateHighestBid = useAuctionStore((state) => state.updateHighestBid);
  const currentWallet = useUserStore((state) => state.currentWallet);
  const getBalance = useUserStore((state) => state.getBalance);
  const addLog = useLogStore((state) => state.addLog);
  const getStatus = useAuctionStore((state) => state.getStatus);

  useEffect(() => {
    // 1. μž…μ°° 이벀트
    const bidEvent = auctionContract.events.BidEvent().on("data", async (event: EventData) => {
      const { highestBidder, highestBid } = event.returnValues;

      const truncatedBidder = highestBidder.substring(0, 7);
      console.log("μž…μ°° 이벀트 데이터:", event.returnValues); // λ””λ²„κΉ…μš© 둜그
      addLog(`[μž…μ°°] ${truncatedBidder}: ${weiToEther(highestBid)} eth `);
      updateHighestBid(Number(highestBid), highestBidder); // μ΅œκ³ κ°€ μž…μ°°κΈˆ κ°±μ‹ 

      if (currentWallet) {
        await getBalance(currentWallet);
      }
    });

    // 2. κ²½λ§€ μ·¨μ†Œ 이벀트
    const cancelEvent = auctionContract.events
      .CanceledEvent()
      .on("data", async (event: EventData) => {
        const { time } = event.returnValues;
        getBalance(currentWallet); // ν˜„μž¬ μ§€κ°‘μ˜ μž”μ•‘ κ°±μ‹ 

        console.log("κ²½λ§€ μ·¨μ†Œ 이벀트 데이터:", event.returnValues); // λ””λ²„κΉ…μš© 둜그
        addLog(`[κ²½λ§€ μ·¨μ†Œ] ${formatTime(time)}`);

        if (currentWallet) {
          await getStatus();
        }
      });

    // 3. κ²½λ§€ μƒνƒœ 이벀트
    const auctionStateEvent = auctionContract.events
      .StateUpdated()
      .on("data", async (event: EventData) => {
        const { message, time } = event.returnValues;

        console.log("κ²½λ§€ μƒνƒœ 이벀트 데이터:", event.returnValues); // λ””λ²„κΉ…μš© 둜그
        addLog(`[μ‹œμŠ€ν…œ] ${message} μ‹œκ°„ : ${formatTime(time)}`);

        await getStatus(); // μ„œλ²„μ˜ μƒνƒœλ₯Ό λ°›μ•„μ™€μ„œ μ΅œμ‹ ν™”
      });

    // 4. 좜금 이벀트
    const withdrawEvent = auctionContract.events
      .WithdrawalEvent()
      .on("data", async (event: EventData) => {
        const { withdrawer, amount } = event.returnValues;

        // withdrawer λ¬Έμžμ—΄μ„ 길이 7둜 μž˜λΌμ€λ‹ˆλ‹€.
        const truncatedWithdrawer = withdrawer.substring(0, 7);

        console.log("좜금 이벀트 데이터:", event.returnValues); // λ””λ²„κΉ…μš© 둜그
        addLog(`[좜금] ${truncatedWithdrawer} : ${weiToEther(amount)} eth`);

        if (currentWallet) {
          await getBalance(currentWallet);
        }
      });

    // 이벀트 λ¦¬μŠ€λ„ˆ 제거
    return () => {
      bidEvent.off();
      cancelEvent.off();
      auctionStateEvent.off();
      withdrawEvent.off();
    };
  }, []);
};

export default useAuctionEvents;

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors