[Web3.js] getTransactionByAccount 함수 구현하기

드림보이즈·2023년 2월 24일

Web3.js

이더리움을 사용한 블록체인 어플을 개발한다는 것은

  • 솔리디티로 스마트 컨트랙트를 개발하거나
  • 블록체인과 상호작용하는 클라이언트 개발

web3.js를 사용하는 것은 후자. 이더 전송 / 스마트 컨트랙트에서 데이터를 읽고 쓰거나 / 스마트 컨트랙트 만드는 등 다양한 액션 수행할 수 있게 하는 라이브러리 집합

클라이언트가 이더리움 블록체인과 상호작용하는 법

JSON RPC를 사용해 소통

Remote Procedure Call 프로토콜

web3.js는 JSON RPC를 사용해 하나의 이더리움 노드에게 요청

JSON RPC 형식에 맞춰서 노드에 데이터를 요청하는 것


getTransactionByAccount 함수 구현하기

web3.js 라이브러리에는 다양한 메서드가 있다.

https://web3js.readthedocs.io/en/v3.0.0-rc.5/web3-eth.html

공식 문서에 설명이 정말 잘되어 있다. 나는 이런거 진짜 안 읽히는데, 여기는 정말 깔끔하고 이해하기 쉽게 설명이 되어있다. 참고하자.

getTransaction, getBlock 등 다양한 메서드가 있는데,

특정 account가 송금받거나, 송금한 정보를 반환하는 함수는 없다.(왜 없지?)

마치 이렇게 내 계정에서 일어난 트랜잭션들을 모아 볼 수 있으면 좋겠다. 이 말이다.

기존 메서드를 조합해서 직접 함수를 만들어보자.

0. 조건

인자로 account, startBlock , endBlock을 세팅해서 블록 범위 내에서 account가 송금을 받거나, 하거나, 트랜잭션을 일으킨 정보들을 가져오는 함수를 만들어보자.

제네시스 블록부터 최신 블록까지 한다면 너무 쿼리가 커져서 구현이 어려울 것이다.

1. getBlock(blockNum)

반환으로 blockNum 내에 있는 트랜잭션 id(해시값)의 배열과, 블록 생성된 시간(timestamp) 정보가 있다. 이 2개를 이용할 것이다.
이 트랜잭션 배열을 map해서 트랜잭션 하나 하나의 정보를 가져오자.

2. getTransaction(txid)

txid를 인자로 넣으면 그 tx의 정보를 받아온다.

from이 트랜잭션을 일으킨 주소 / to가 받는 주소 / value가 금액이다.

즉 우리는 from이나, to가 account랑 일치하는지 체크하고,

그에 따라 정보를 출력해주면 되는 것이다.

원래 송금 정보만 출력해주면 되지만, 송금말고도 컨트랙트 생성, 컨트랙트 함수 실행 등에서도 트랜잭션이 발생한다.

account가 from과 일치하는데, to가 없다(null)? : 컨트랙트를 배포했다는 뜻

이것도 고려해서 송금 내역과, 컨트랙트 배포를 구분해서 알려주는 함수를 구현하자.

(이것 말고도 함수 실행이나 이런 것들을 알려주면 좋을텐데, 어떻게 구분하는지, 또 어떤 경우에 트랜잭션이 발생하는지 정확히 모르겠다;;)

코드 구현

로직은 너무 간단하다.

  • for문으로 start ~ end 블록 범위를 세팅한다.
  • for문을 돌리면서 getBlock()을 실행한다.
  • getBlock()의 반환값 중 transaction 배열을 map을 돌려 각각 getTransaction을 실행
  • getTransaction() 반환값 중 from, to와 account를 비교해서 어떤 트랜잭션인지 구분하고 사용자에게 알려준다.
const Web3 = require("web3");
const rpcURL = "https://goerli.infura.io/v3/개인키";

const web3 = new Web3(rpcURL);

async function getTransactionByAccount(account, startBlock, endBlock) {
  for (let block = startBlock; block <= endBlock; block++) {
    blockJson = await web3.eth.getBlock(block);
    const { transactions, timestamp } = blockJson;
    const date = new Date(timestamp * 1000);
    var year = date.getFullYear().toString().slice();
    var month = ("0" + (date.getMonth() + 1)).slice(-2);
    var day = ("0" + date.getDate()).slice(-2);

    var returnDate = year + "년 " + month + "월 " + day + "일";

    transactions.map((tx) => {
      web3.eth.getTransaction(tx).then((obj) => {
        const from = obj.from;
        const to = obj.to;
        const value = web3.utils.fromWei(obj.value, "ether");

        if (from == account) {
          if (to == null) {
            console.log(
              `${returnDate}에 스마트 컨트랙트를 배포한 기록이 있습니다.`
            );
          } else {
            console.log(
              `${returnDate}${to} 주소로 ${value}ETH를 송금한 기록이 있습니다.`
            );
          }
        } else if (to == account) {
          console.log(
            `${returnDate}${from} 주소로부터 ${value}ETH를 송금 받은 기록이 있습니다.`
          );
        }
        return;
      });
    });
  }
}

getTransactionByAccount(
  "0x20a77e56117Df0512aD363e86BBa4A556d3d6F87",
  8533863,
  8533867
);

참 간단한데, 뭔가 대단한 걸 만든 기분이 든다. 그대로 web3.js team에게 추가하라고 알려주면 되나?


더 알아보기

Q. 트랜잭션이 일어나는 모든 경우를 알아보라


Q. 블록 탐색 범위가 늘어날수록 엄청 느리다. 이걸 어떻게 최적화할 수 있을까?


Q. 왜 이더 스캔은 ㅈㄴ 빠를까? 모든 블록을 다 뒤지는데 속도가 말이 안된다.

A. 이더스캔은 많은 서버를 사용하여 데이터를 저장하고, 쿼리 최적화, DB 인덱스화를 하여 데이터를 효율적으로 처리할 수 있도록 구현함.

profile
시리즈 클릭하셔서 카테고리 별로 편하게 보세용

0개의 댓글