가스 최적화 후 느린 트랜잭션 처리
포스트
취소

가스 최적화 후 느린 트랜잭션 처리

회사에서 NFT 민팅 작업을 하는데, 민팅 로직을 개선한 결과가 너무 마음에 들었다. 결론적으로, 트랜잭션 해시값을 미리 가져와서 민팅을 비동기적으로 진행할 수 있게 만들었다.


배경

회사에서 이더리움 & 폴리곤 메인넷에 NFT 민팅하는 업무를 맡았다.


처음으로 진행하는 메인넷 배포 & 민팅에 설레이면서도 불안함을 가지면서 작업을 진행했다.


폴리곤의 경우, MATIC의 가격이 높지 않아서 민팅에 부담이 없었지만


이더리움의 경우, 가격이 높아서 그런지 아무리 회사 비용이라고는 해도 부담이 컸다. (이더리움은 오늘 일자(2/11)로 가격이 약 193 만원 정도이다.)


개인적으로 블록체인 개발자는 최대한 회사에서 블록체인으로 인한 지출을 줄여줄 수 있게끔 도와주는 역할이라 생각한다.


이 돈을 아끼고 싶어서 민팅할 때에 최대한 가스비를 적게 설정하고 트랜잭션을 진행하는데, 민팅 한번할 때마다 약 1분 정도를 소요했다.


다행히 기획했던 NFT에 대한 민팅은 잘 진행했지만, 개발자로서 개선해야할 부분이 너무 확연해서 좀 더 디벨롭하기로 한다.



이슈

이번 작업에서 개선해야 할 부분에 대한 리스트업을 해보았다.

1. 가스 최적화 - 어떻게 해야 최적의 가스비를 산출할까?

이번에 내가 맡은 NFT 민팅 임무는 두 가지 상황에서 이루어진다.

  • (1) NFT 체험 행사 때 실시간으로 참여자들의 작품을 민팅
    • 속도가 생명. 가스비 ↑
    • 메인 체인 : Polygon
  • (2) 이더리움 네트워크에 대표 작품 19작을 행사 전에 미리 민팅
    • 미리 진행하는 것이다보니 가스비는 최소한으로 ↓

각 상황에 맞게 가스비를 산정하여 진행해야 한다.


2. 동기적인 트랜잭션 코드

문제는 내가 작성한 코드가 동기적이다보니 트랜잭션이 해시값을 리턴해줘야 민팅 과정이 끝난다는 것이다.


스크린샷 2022-12-09 오후 1 30 33


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 동기적인 코드(해시 리턴 값을 가져오기까지 오래걸림)
    await web3.eth.accounts
      .signTransaction(
        {
          from: REACT_APP_ADDRESS,
          to: to,
          gasPrice: 5000000, // 이더리움으로 진행할 시 1000000
          data: data,
        },
        privateKey
      )
      .then(async (Tx) => {
        await web3.eth
          .sendSignedTransaction(Tx.rawTransaction)
          .then((hash, err) => {
            if (err) {
              console.log(err);
            } else {
              console.log("민팅이 완료되었습니다.");
              return hash;
            }
          });
      });


  • 동기적 로직의 아쉬운 점 :
    • 트랜잭션이 담긴 블록이 연결되기까지 기다려야 해서 서비스 단의 속도가 느리다.

      블록이 블록체인 네트워크에 추가되는 과정 스크린샷 2022-12-09 오후 1 30 33

    만약 이더리움 고얼리(goerli) 테스트넷으로 트랜잭션을 동기적으로 보내면 약 1분 정도 소요된다.

3. (Optional) Upgradeable Contract

CTO님이 무심결에 지나가면서 하신 말씀이 있다. “민팅은 아무 주소나 할 수 있어요?”


내가 만든 컨트랙트는 onlyOwner가 붙어있어서 최초에 컨트랙트를 배포한 주소가 아니면 민팅을 할 수가 없기 때문에 “가능하게 만들까요?” 라고 말씀드렸지만


“아니 이번에 진행할 때에는 크게 상관없을 것 같으니 넘어갑시다.” 라고 하셨다.


하지만, 개발자로서 필요함이 1%라도 있으면 준비해놔야 겠다는 생각이 들었다.



해결과정

이 세가지의 이슈들을 해결해보자.

1. 가스 최적화 - 어떻게 해야 최적의 가스비를 산출할까?

참고1, 참고2

사실 이 부분은 “내가 해결했다.” 라기 보다는 “안썼던 web3.js의 기능을 갖다썼다.” 라고 하는 것이 더 맞는 말이다.


(ether.js 에도 있겠지만) web3.js에는 estimateGas라는 기능이 있다.


이 기능은 사용자가 설정한 트랜잭션 옵션을 토대로 로컬 블록체인에 돌려보고 나오는 가스비들을 Binary Search 알고리즘 통해 최적의 값으로 산출해내는 기능이다.


1
2
3
4
5
6
7
8
// 내 컨트랙트 기능을 미리 실행해보는 estimateGas
const estimate = await MyMetaGalleryContract.mintNFT(
    REACT_APP_ADDRESS,
    tokenURI
  ).estimateGas({
    from: REACT_APP_ADDRESS,
    gas: 5000000,
  });


2. 동기적인 트랜잭션 코드

참고

‘어떻게 하면 트랜잭션 플로우를 좀 더 효율적으로 사용할 수 있을까?’에 대한 생각을 하면서 이리저리 트랜잭션 테스트를 해보던 중


트랜잭션 해시값은 미리 리턴이 되고, Scan으로 확인해보면 <pending> 처리 되어있는 것을 확인했다.


‘어차피 트랜잭션은 블록체인에 올라가 있을 것이고, 노드가 처리를 하느냐 마느냐의 문제인가?’ 가 내가 내린 결론이다.(틀리면 말씀주세요 ㅎ)


그렇다면, 트랜잭션 해시값을 미리 받아올 수 있다면? 그 후 플로우는 블록체인에 넘기면 되는 것 아닌가!?


이렇게 결론이 마친 후 코드를 수정하였다.


스크린샷 2022-12-09 오후 1 30 33


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 사인한 트랙잭션에 대한 해시를 미리 넘겨서 빨리 끝낸다.
  const signTx = await web3.eth.accounts
    .signTransaction(
      {
        from: REACT_APP_ADDRESS,
        to: to,
        gas: estimateGas,
        data: data,
      },
      privateKey
    )
    .then((res) => {
      return res.rawTransaction;
    });

  const txHash = web3.utils.sha3(signTx); // 이 부분이 내 트랜잭션 해시값을 얻어낼 수 있는 바로 그곳.

  const result = {
    hash: txHash,
    tx: signTx,
  };

  return result;



결과

회사 프로젝트라 메인넷 스크린샷을 찍을 수 없었습니다.

결과적으로 민팅은 최적화된(빠르고 값이 최소화) 가스비로, 민팅을 진행하게 되면 비동기적으로 주문을 올려놓는 형식이 되었다.


민팅을 하고 기다리면 오픈씨에 민팅된 결과물이 뜬다. 다음은 개선된 결과가 만족스러운 폭풍 민팅의 결과다.

스크린샷 2022-12-09 오후 1 30 33


  • (+Optional) Upgradeable Contract
    • 프록시 컨트랙트 성공했는데 포스트가 너무 길어져 다음으로 넘길게요.
    • 레포



느낀점

나는 Developer(개선하는 사람) 이다!

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.