본문 바로가기
JavaScript

[JavaScript] 블록 익스플로러에 비트코인 조회 기능 추가하기

by teamnova 2024. 1. 9.

 

https://stickode.tistory.com/1016

https://stickode.tistory.com/1023

 

이전 게시글들에 이어 이더리움 익스플로러에 비트코인 조회 기능을 추가해서 여러 블록체인을 조회할 수 있는 블록익스플로러로 확장해보겠습니다. infura key 같은 경우는 위 게시글을 참고하시면 쉽게 하실 수 있습니다.

 

 

 

여기부터는 프로젝트 파일 구조 및 코드입니다

프로젝트 구조. 파일들을 이미지와 같이 배치하세요

 

index.html

<!--public/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>Cryptocurrency Block Explorer</title>
</head>
<body>

<h1>Cryptocurrency Block Explorer</h1>

<!-- 이더리움 블록 조회 섹션 -->
<h2>Ethereum Block Lookup</h2>
<input type="number" id="ethBlockNumber" placeholder="Ethereum Block Number" />
<button onclick="getEthBlock()">Get Ethereum Block</button>
<br>

<!-- 이더리움 트랜잭션 및 계정 조회 섹션 -->
<h2>Ethereum Transaction and Account Lookup</h2>
<input type="text" id="ethTransactionHash" placeholder="Ethereum Transaction Hash" />
<button onclick="getEthTransaction()">Get Transaction</button>
<br>

<input type="text" id="ethAccountAddress" placeholder="Ethereum Account Address" />
<button onclick="getEthBalance()">Get Balance</button>
<br>

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


<!-- 비트코인 블록 조회 섹션 -->
<h2>Bitcoin Block Lookup</h2>
<input type="number" id="btcBlockHeight" placeholder="Bitcoin Block Height" />
<button onclick="getBtcBlock()">Get Bitcoin Block</button>
<br>

<!-- 비트코인 트랜잭션 및 주소 조회 섹션 -->
<h2>Bitcoin Transaction and Address Lookup</h2>
<input type="text" id="btcTransactionHash" placeholder="Bitcoin Transaction Hash" />
<button onclick="getBtcTransaction()">Get Bitcoin Transaction</button>
<br>

<input type="text" id="btcAddress" placeholder="Bitcoin Address" />
<button onclick="getBtcBalance()">Get Bitcoin Balance</button>
<br>

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

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

        try {
            // 서버로부터 블록 정보를 조회하는 HTTP 요청
            const response = await fetch(`/ethereum/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 getEthTransaction() {
        const ethTransactionHash = document.getElementById('ethTransactionHash').value;
        if (!ethTransactionHash) {
            alert('Please enter a transaction hash');
            return;
        }
        try {
            const response = await fetch(`/ethereum/transaction/${ethTransactionHash}`);
            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 getEthBalance() {
        const ethAccountAddress = document.getElementById('ethAccountAddress').value;
        if (!ethAccountAddress) {
            alert('Please enter an account address');
            return;
        }
        try {
            const response = await fetch(`/ethereum/balance/${ethAccountAddress}`);
            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 getEthGasPrice() {
        try {
            const response = await fetch('/ethereum/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.');
        }
    }

    async function getBtcBlock() {
        const blockHeight = document.getElementById('btcBlockHeight').value;
        if (!blockHeight) {
            alert('Please enter a Bitcoin block height');
            return;
        }

        try {
            const response = await fetch(`/bitcoin/block/${blockHeight}`);
            const blockData = await response.json();
            document.getElementById('result').textContent = JSON.stringify(blockData, null, 2);
        } catch (error) {
            console.error('Error fetching Bitcoin block:', error);
            alert('Error fetching Bitcoin block. Check the console for more details.');
        }
    }

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

    async function getBtcBalance() {
        const address = document.getElementById('btcAddress').value;
        if (!address) {
            alert('Please enter a Bitcoin address');
            return;
        }
        try {
            const response = await fetch(`/bitcoin/address/${address}`);
            const balance = await response.json();
            document.getElementById('result').textContent = `Balance: ${balance.balance}`;
        } catch (error) {
            console.error('Error fetching Bitcoin balance:', error);
            alert('Error fetching Bitcoin balance. Check the console for more details.');
        }
    }
</script></body>
</html>

 

 

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('/ethereum/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('/ethereum/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('/ethereum/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('/ethereum/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 상태 코드와 에러 메시지 전송
    }
});

// 비트코인 블록 조회 라우트
app.get('/bitcoin/block/:blockHeight', async (req, res) => {
    try {
        const blockHeight = req.params.blockHeight;
        console.log(`[Bitcoin Block Request] Height: ${blockHeight}`); // 로그에 블록 높이 출력
        const response = await fetch(`https://blockchain.info/block-height/${blockHeight}?format=json`);
        const blockData = await response.json();
        console.log("Bitcoin block data: ", blockData); // 조회한 블록 데이터 로그에 출력
        res.json(blockData);
    } catch (error) {
        console.error('Error fetching Bitcoin block:', error);
        res.status(500).send(error.toString());
    }
});

// 비트코인 트랜잭션 조회 라우트
app.get('/bitcoin/transaction/:transactionHash', async (req, res) => {
    try {
        const transactionHash = req.params.transactionHash;
        console.log(`[Bitcoin Transaction Request] Hash: ${transactionHash}`); // 로그에 트랜잭션 해시 출력
        const response = await fetch(`https://blockchain.info/rawtx/${transactionHash}`);
        const transactionData = await response.json();

        if (response.status !== 200) {
            throw new Error('Transaction not found');
        }

        console.log("Bitcoin transaction data: ", transactionData); // 조회한 트랜잭션 데이터 로그에 출력
        res.json(transactionData);
    } catch (error) {
        console.error('Error fetching Bitcoin transaction:', error);
        res.status(500).send(error.toString());
    }
});

// 비트코인 주소 잔액 조회 라우트
app.get('/bitcoin/address/:address', async (req, res) => {
    try {
        const address = req.params.address;
        console.log(`[Bitcoin Address Request] Address: ${address}`); // 로그에 주소 출력
        const response = await fetch(`https://blockchain.info/rawaddr/${address}`);
        const addressData = await response.json();

        if (response.status !== 200) {
            throw new Error('Address not found');
        }

        console.log("Bitcoin address data: ", addressData); // 조회한 주소 데이터 로그에 출력
        res.json({ balance: addressData.final_balance });
    } catch (error) {
        console.error('Error fetching Bitcoin address:', error);
        res.status(500).send(error.toString());
    }
});


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

 

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"
  }
}