본문 바로가기
JavaScript

[JavaScript] 이더리움 익스플로러에 조회 기능 추가하기

by teamnova 2024. 1. 4.
728x90

 

 

저번 게시글(https://stickode.tistory.com/1016)에 이어서 오늘은 이더리움 블록익스플로러에 트랜잭션과 계정의 잔액, 그리고 현재 가스 가격을 조회하는 기능을 추가했습니다. 

 

 

 

프로젝트 구조

 

 

 

 

 

코드

app.js

// app.js

const express = require('express'); // Express 모듈 불러오기
const app = express(); // Express 애플리케이션 생성
const { Web3 } = require('web3'); // Web3 모듈 불러오기
const web3 = new Web3('https://mainnet.infura.io/v3/발급받은 키'); // Infura를 통해 이더리움 메인넷에 연결

// BigInt 값을 문자열로 변환하여 JSON으로 직렬화하는 함수
function serializeBigInt(key, value) {
    return typeof value === 'bigint' ? value.toString() : value; // 만약 값이 BigInt 타입이면 문자열로 변환
}

app.use(express.static('public')); // 'public' 디렉토리의 정적 파일을 제공

// 블록 번호를 이용해 블록 정보를 조회하는 라우트
app.get('/block/:blockNumber', async (req, res) => {
    try {
        console.log(" blockNumber : " + req.params.blockNumber); // 로그에 블록 번호 출력
        const block = await web3.eth.getBlock(req.params.blockNumber); // 비동기적으로 블록 정보 조회
        console.log(" block : "); // 로그에 블록 정보 시작을 알림
        console.log(block); // 조회한 블록 정보를 로그에 출력
        const blockString = JSON.stringify(block, serializeBigInt); // BigInt 처리를 위한 직렬화 함수와 함께 JSON으로 변환
        res.setHeader('Content-Type', 'application/json'); // 응답 헤더에 컨텐츠 타입 설정
        res.send(blockString); // 직렬화된 블록 정보를 응답으로 전송
    } catch (error) {
        console.error(error); // 에러 발생시 콘솔에 에러 출력
        res.status(500).send(error.toString()); // 클라이언트에 500 상태 코드와 에러 메시지 전송
    }
});

// 트랜잭션 해시를 이용해 트랜잭션 정보를 조회하는 라우트
app.get('/transaction/:transactionHash', async (req, res) => {
    const transactionHash = req.params.transactionHash; // 요청에서 트랜잭션 해시 추출
    console.log(`[Transaction Request] Hash: ${transactionHash}`); // 로그에 트랜잭션 요청 해시 출력
    try {
        const transaction = await web3.eth.getTransaction(req.params.transactionHash); // 트랜잭션 정보 조회
        // BigInt 처리를 위한 직렬화 함수와 함께 JSON으로 변환
        const transactionString = JSON.stringify(transaction, serializeBigInt);
        res.setHeader('Content-Type', 'application/json'); // 응답 헤더에 컨텐츠 타입 설정
        res.send(transactionString); // 직렬화된 트랜잭션 정보를 응답으로 전송
    } catch (error) {
        console.error(error); // 에러 발생시 콘솔에 에러 출력
        res.status(500).send(error.toString()); // 클라이언트에 500 상태 코드와 에러 메시지 전송
    }
});

// 계정 주소를 이용해 계정 잔액을 조회하는 라우트
app.get('/balance/:accountAddress', async (req, res) => {
    const accountAddress = req.params.accountAddress; // 요청에서 계정 주소 추출
    console.log(`[Balance Request] Account address: ${accountAddress}`); // 로그에 계정 잔액 요청 주소 출력
    try {
        const balance = await web3.eth.getBalance(req.params.accountAddress); // 계정 잔액 조회
        // BigInt 처리를 위한 직렬화 함수와 함께 JSON으로 변환
        const balanceString = JSON.stringify({ balance }, serializeBigInt);
        res.setHeader('Content-Type', 'application/json'); // 응답 헤더에 컨텐츠 타입 설정
        res.send(balanceString); // 직렬화된 계정 잔액을 응답으로 전송
    } catch (error) {
        console.error(error); // 에러 발생시 콘솔에 에러 출력
        res.status(500).send(error.toString()); // 클라이언트에 500 상태 코드와 에러 메시지 전송
    }
});

// 현재 가스 가격을 조회하는 라우트
app.get('/gasPrice', async (req, res) => {
    console.log(`[Gas Price Request] Request for current gas price`); // 로그에 현재 가스 가격 요청 출력
    try {
        const gasPrice = await web3.eth.getGasPrice(); // 현재 가스 가격 조회
        // BigInt 처리를 위한 직렬화 함수와 함께 JSON으로 변환
        const gasPriceString = JSON.stringify({ gasPrice }, serializeBigInt);
        res.setHeader('Content-Type', 'application/json'); // 응답 헤더에 컨텐츠 타입 설정
        res.send(gasPriceString); // 직렬화된 가스 가격을 응답으로 전송
    } catch (error) {
        console.error(error); // 에러 발생시 콘솔에 에러 출력
        res.status(500).send(error.toString()); // 클라이언트에 500 상태 코드와 에러 메시지 전송
    }
});

const PORT = 3000; // 서버가 리스닝할 포트 번호
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`); // 서버 시작시 로그에 실행 주소 출력
});

 

 

index.html

<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ethereum Block Explorer</title>
</head>
<body>
<h1>Ethereum Block Explorer</h1>

<input type="number" id="blockNumber" placeholder="Block Number" />
<button onclick="getBlock()">Get Block</button>
<br>
<input type="text" id="transactionHash" placeholder="Transaction Hash" />
<button onclick="getTransaction()">Get Transaction</button>
<br>

<input type="text" id="accountAddress" placeholder="Account Address" />
<button onclick="getBalance()">Get Balance</button>
<br>

<button onclick="getGasPrice()">Get Current Gas Price</button>
<br>

<pre id="result"></pre>

<script>
    async function getBlock() {
        // 입력 필드에서 블록 번호를 가져옴
        const blockNumber = document.getElementById('blockNumber').value;
        // 블록 번호가 입력되지 않았다면 경고 메시지 출력 후 함수 종료
        if (!blockNumber) {
            alert('Please enter a block number');
            return;
        }

        try {
            // 서버로부터 블록 정보를 조회하는 HTTP 요청
            const response = await fetch(`/block/${blockNumber}`);
            // HTTP 응답 본문을 텍스트로 읽음
            const blockString = await response.text();
            // 응답 텍스트를 JSON 객체로 파싱
            const block = JSON.parse(blockString, (key, value) => {
                // 문자열이 'n'으로 끝나면 BigInt로 변환
                if (typeof value === 'string' && value.endsWith('n')) {
                    return BigInt(value.slice(0, -1));
                }
                return value;
            });
            // 블록 정보를 콘솔에 출력
            console.log(" block : ");
            console.log(block);
            // 결과 영역에 블록 정보를 표시
            document.getElementById('result').textContent = JSON.stringify(block, null, 2);
        } catch (error) {
            // 오류 발생 시 콘솔에 오류 메시지 출력 및 사용자에게 알림
            console.error('Error fetching block:', error);
            alert('Error fetching block. Check the console for more details.');
        }
    }

    async function getTransaction() {
        const transactionHash = document.getElementById('transactionHash').value;
        if (!transactionHash) {
            alert('Please enter a transaction hash');
            return;
        }
        try {
            const response = await fetch(`/transaction/${transactionHash}`);
            const transaction = await response.json();
            document.getElementById('result').textContent = JSON.stringify(transaction, null, 2);
        } catch (error) {
            console.error('Error fetching transaction:', error);
            alert('Error fetching transaction. Check the console for more details.');
        }
    }

    async function getBalance() {
        const accountAddress = document.getElementById('accountAddress').value;
        if (!accountAddress) {
            alert('Please enter an account address');
            return;
        }
        try {
            const response = await fetch(`/balance/${accountAddress}`);
            const balance = await response.json();
            document.getElementById('result').textContent = `Balance: ${balance.balance}`;
        } catch (error) {
            console.error('Error fetching balance:', error);
            alert('Error fetching balance. Check the console for more details.');
        }
    }

    async function getGasPrice() {
        try {
            const response = await fetch('/gasPrice');
            const gasPrice = await response.json();
            document.getElementById('result').textContent = `Current Gas Price: ${gasPrice.gasPrice}`;
        } catch (error) {
            console.error('Error fetching gas price:', error);
            alert('Error fetching gas price. Check the console for more details.');
        }
    }
</script>
</body>
</html>

 

package.json

{
  "name": "js-search-block-info-ethereum",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "express": "^4.18.2",
    "web3": "^4.2.1"
  }
}

 

 

 

실행영상

 

 

트랜잭션 및 계정 조회 시 참고한 사이트

https://etherscan.io/