목차

반응형

증상

nodejs의 web3 라이브러리를 사용하여 스마트컨트랙트로 구현한 이벤트를 구독하는데 시간이 지나면 이벤트 리스닝이 끊기는 경우가 있다.

 

원인

원인은 web3 프로바이더를 설정할때 옵션으로 프로바이더의 통신이 끊기지 않도록 설정해주지 않아서 그렇다.

 

인터넷에 보통 아래 2개 형태의 코드가 떠돌아다니는데 프로바이더 설정에 대한 내용이 빠져있다.

const Web3 = require('web3');
require('dotenv').config()

// ENTER A VALID RPC URL!
const web3 = new Web3(process.env.NODE_URL);

//ENTER SMART CONTRACT ADDRESS BELOW. see abi.js if you want to modify the abi
const CONTRACT_ADDRESS = "0xE1C8f3d529BEa8E3fA1FAC5B416335a2f998EE1C";
const CONTRACT_ABI = require('./abi.json');
const contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);

async function getEvents() {
    let latest_block = await web3.eth.getBlockNumber();
    let historical_block = latest_block - 10000; // you can also change the value to 'latest' if you have a upgraded rpc
    console.log("latest: ", latest_block, "historical block: ", historical_block);
    const events = await contract.getPastEvents(
        'Transfer', // change if your looking for a different event
        { fromBlock: historical_block, toBlock: 'latest' }
    );
    await getTransferDetails(events);
};

async function getTransferDetails(data_events) {
    for (i = 0; i < data_events.length; i++) {
        let from = data_events[i]['returnValues']['from'];
        let to = data_events[i]['returnValues']['to'];
        let amount = data_events[i]['returnValues']['amount'];
        let converted_amount = web3.utils.fromWei(amount);
        if (converted_amount > 32) { //checking for transcations with above 32 eth as an example
            console.log("From:", from, "- To:", to, "- Value:", converted_amount);
        }
    };
};


getEvents(CONTRACT_ABI, CONTRACT_ADDRESS);
var Web3 = require('web3');
var abi = '...';
var contractAddress = '0x62e7Dd1Af52d5A08D401b1e156cC4CB1d2f89d57';
var eventName = 'Transfer';
//var web3;
var etat;
const web3 = new Web3(new Web3.providers.WebsocketProvider('http://127.0.0.1:8545'));
/*
var MyContract = new web3.eth.Contract(JSON.parse(abi));
var myContractInstance = MyContract.at('0x78e97bcc5b5dd9ed228fed7a4887c0d7287344a9');

// watch for an event with {some: 'args'}
var myEvent = myContractInstance.Transfer({}, {fromBlock: 0, toBlock: 'latest'});
myEvent.watch(function(error, result){
    console.log(result)
});

// would get all past logs again.
var myResults = myEvent.get(function(error, logs){ console.log(logs) });



// would stop and uninstall the filter
myEvent.stopWatching();
*/



var TokenContract = new web3.eth.Contract(JSON.parse(abi),contractAddress);
TokenContract.events.allEvents({ fromBlock: 'latest' }, console.log)
console.log('1********************************************************************************')
console.log(TokenContract);
console.log('2********************************************************************************')
var event = TokenContract.events.Transfer();
console.log(event);
console.log('3********************************************************************************')

TokenContract.once('Transfer', {
}, function(error, event){ console.log(event); });

event.watch(function(error, result){
    if (!error) {
        alert("wait for a while, check for block Synchronization or block creation");
        console.log(result);
        console.log('pas d erreur');
    }else {
        console.log(error);
        console.log('erreur')
    }
});

 

해결방법

웹3 프로바이더 인스턴스 생성 시 옵션 정보를 넣어주는 것으로 해결 가능하다.

아래와 같이 코드를 짜면 해결된다. 추가로 web3-provider-ws 라는 라이브러리를 사용했다.

var express = require('express');
var router = express.Router();
const { item, itemPreset } = require('../models');
const debug = (...messages) => console.log(...messages)

var Web3WsProvider = require('web3-providers-ws');
var Web3 = require('web3');
const options = {
    timeout: 30000, // ms

    clientConfig: {
        // Useful if requests are large
        maxReceivedFrameSize: 100000000,   // bytes - default: 1MiB
        maxReceivedMessageSize: 100000000, // bytes - default: 8MiB

        // Useful to keep a connection alive
        keepalive: true,
        keepaliveInterval: 60000 // ms
    },

    // Enable auto reconnection
    reconnect: {
        auto: true,
        delay: 5000, // ms
        maxAttempts: 5,
        onTimeout: false
    }
};

var web3 = new Web3();
web3.setProvider(new Web3WsProvider('wss://speedy-nodes-nyc.moralis.io/12341234/bsc/mainnet/ws', options));

const contract = require("../artifacts/contracts/Furniture.sol/Furniture.json")
const googleCloudStorage = require("../scripts/googleCloudStorage");
const contractAddress = process.env.contractAddress
const nftContract = new web3.eth.Contract(contract.abi, contractAddress)


var eventMintFurniture = nftContract.events.eventMint({}).on("data", async event => {
    console.log("Mint")
    let result = event.returnValues
    let msgSender = result[0]
    let tokenId = parseInt(result[1])
    // ... your logic

}).on("connected", event => {
    console.log('eventMintFurniture connected')
}).on("error", event => {
    console.log('eventMintFurniture error')
    console.log(event)
}).on("end", event => {
    refreshProvider(web3, 'wss://ropsten.infura.io/ws/v3/925d17c78a0b41f5907df58579ce44bd')
    console.log("eventMintFurniture end")
})

 

 

참고

https://web3js.readthedocs.io/en/v1.5.2/web3-eth.html#configuration

반응형