web3js 监控以太坊代币交易

监控以太坊交易记录,监控以太坊代币交易;

如题: 如何监控以太坊交易记录

一般都是监控代币转账记录;

erc20标准的转账

//转账方法
function transfer(address to, uint256 value) public returns (bool);
function transferFrom(address from, address to, uint256 value) public returns (bool);
//转账事件,以上两个交易成功都会发送该事件
event Transfer(address indexed from, address indexed to, uint256 value);

监控方式

  1. 遍历块信息,匹配to是否是某代币,再匹配input或者log,是否是transfer/transferFrom
  2. 使用websocket方式链接节点,通过订阅的方式
  3. 使用etherscan.io/apis
  4. 本文主要介绍的logs方式

    1、扫块方式

let transaction = await web3.eth.getTransaction(pHash).catch(Common.thenCatch);
if (tr.to === null) {
   //创建智能合约,
}else if(tr.input.length >= 10){
    //调用合约方法
    //再匹配是否调用了某方法
    if (input.startsWith('0xa9059cbb')) {
        //transfer(address,uint256)
      }else if(input.startsWith('0x23b872dd'){
        //transferFrom
      }
}else{
    //转账以太坊
}

如果疑惑0xa9059cbb是什么,可参考链接 以太坊实战【地址监控三】

或者参考 理解以太坊时间与日志 

web3.sha3("transfer(address,uint256)")

推荐一个通过abi解析input的库 https://lab.miguelmota.com/ethereum-input-data-decoder/example/

2、订阅的方式监控块

 这个方式会比较简单,但是链接节点需要通过ws的方式。ws好像比较耗资源..链接数多了节点会卡;

使用方法:

1)合约事件订阅

   参考 http://cw.hubwiz.com/card/c/web3.js-1.0/1/4/13/

    let web3 = AppConfig.getWeb3();
    let contract = new web3.eth.Contract(AppConfig.getErc20Abi(),addr);
    let events = new Array();
    //contract.events.Transfer({fromBlock:0,filter:{to:to}},function (error,event) {
    contract.events.Transfer({fromBlock:0,filter:{from:from}},function (error,event) {
        //
        console.log(event);
    }).on('data',function (log) {
        console.log(log);
    }).on('changed',function (log) {
        console.error('changed-----');
        console.log(log);
    }).on('error',function (log) {
        console.error('error-----');
        console.log(log);
    });

2)直接使用web3.eth订阅事件

 参考http://cw.hubwiz.com/card/c/web3.js-1.0/1/3/6/

    let obj = {
        // address:'替换'
        topics: ['替换']
    };
    let web3 = ConfigInit.getWeb3();
    subscription = web3.eth.subscribe('logs', obj, function (err, result) {
        console.log(err);
        console.log(result);
    }).on("data", function (log) {
        console.error('data-----');
        console.log(log);
    }).on("changed", function (log) {
        console.error('changed-----');
        console.log(log);
    }).on('error', function (log) {
        console.error('error-----');
        console.log(log);
    });

3、使用etherscan-apis 

  具体查看  https://etherscan.io/apis

  使用访问限制,看提示是每秒最多5个请求..具体看实测。。那个MyApiKey也可以不注册使用

使用例子

//测试网
https://api-rinkeby.etherscan.io/api?module=account&action=tokentx&contractaddress=0x00000000&startblock=4936100&endblock=4936200&sort=asc
//主网
https://api.etherscan.io/api?module=account&action=tokentx&contractaddress=0x00000000&startblock=4936100&endblock=4936200&sort=asc
//自行替换里面的合约地址和start,end块号

4、通过web3.eth.getPastLogs

 该方法是无意中发现的..之前再群里总问别人,除了上述三种方式,还有没有其他的...没人答.... 

然后还去找go-ethereum 看有没有方案,忘了后面怎么发现了getPastLogs

先说下为啥不用上面三种

第一种方式:扫to;会有一种地方会漏掉。 比如批量转代币(不懂的可以查看 https://blog.csdn.net/zgf1991/article/details/90756619),或者其他合约内部转账

使用批量合约转币,to是批量合约工具的地址,调用的方法也不是transfer(address,uint256);未知的,合约开发者随意取名,不过内部还是掉的transfer

第二种方式:ws是因为我这边没有测试节点,主节点怕用了ws卡或搞坏环境;

第三种方式:etherscan-apis;不稳定..万一请求过快/过多。被限制或被检测是爬虫; 就GG了。群里有个小伙用这个不是很稳定,还出404~~

好了。正题。。

目前我使用getPastLogs挺好用的, 每隔45-60s调用一次,自行保存最后一次的块号;

如使用发现问题..请告知一声

核心代码如下


MonitorTokensTxLogsUtils.scanBlockTokenTx = async function (startBlock, endBlock) {

    let arr = await MongodbOptions.getAllContract().catch(function (e) {
        log4js.error('查询mongodb合约列表出错 ' + e.message);
    });
    if (arr === undefined) {
        //查询出错
        return null;
    }
    if (arr.length === 0) {
        log4js.info('未查询到mongodb合约数据');
    } else {
        let map = new Map();
        for (let i = 0; i < arr.length; i++) {
            map.set(arr[i].address, arr[i].name);
        }
        await getLogs(map, startBlock, endBlock);
    }
    return true;

};

/**
 * 扫区块,监控入账
 * 只有交易成功的才会进入到这里,交易失败的,没有
 * @param tokenMap map(addr=>name);存入时需转成小写! 先从数据库查询
 * @param startBlock
 * @param endBlock
 */
async function getLogs(tokenMap, startBlock, endBlock) {
    let web3 = ConfigInit.getWeb3();
    let datas = await web3.eth.getPastLogs({fromBlock: startBlock, toBlock: endBlock, topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']}).catch(function (e) {
        log4js.error('查询交易记录出错:startBlock: ' + startBlock + ' -> endBlock: ' + endBlock);
        return null;
    });
    console.log('查询到' + datas.length + '条数据');
    for (let i = 0; i < datas.length; i++) {
        let item = datas[i];
        let addr = item.address.toLocaleLowerCase();
        console.log(addr);
        if (tokenMap.has(addr)) {
            let to = '0x' + item.topics[2].substring(26);//32字节,20是地址,还有12是补0,加上0x;12*2 = 24 + 2 = 26;
            to = to.toLocaleLowerCase();
            let isAddrExist = await MongodbOptions.checkToAddr(to).catch(Common.thenCatch);
            if (isAddrExist) {
                let tx = item.transactionHash.toLocaleLowerCase();
                let logIndex = item.logIndex;
                //确认交易是否已经存在;
                let txExist = await checkTxExist(tx, logIndex);
                if (txExist) {
                    log4js.info('交易已经存在 ' + tx + ' ' + logIndex + ' ' + to);
                    continue;
                }
                let blockHash = item.blockHash;
                let blockHeight = item.blockNumber;
                let value = web3.utils.fromWei(item.data);
                let blockTimestamp = await getBlockTime(blockHeight);
                reportContract(tx, blockHash, blockHeight, to, value, endBlock, blockTimestamp, logIndex, tokenMap.get(addr));
            }
        }
    }
    return true;
}

如果疑问 topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] 是什么;

借鉴下 理解以太坊事件与日志  文中的代码

> web3.sha3("Transfer(address,address,uint256)")
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

topics 内容结构如何解析,可以先自己测试获取成功,去看实际返回值分析;

参考批量日志 https://ropsten.etherscan.io/tx/0x630b868e41ecfbd97273371ed33242439230355d9110095b814ae3029b88d829#eventlog

该记录不是我的...网上抠的~~~

说下我目前的了解:

返回的datas是所有发送了Transfer(address,address,uint256)事件的交易记录;主要都是ERC20代币

topics[0]是事件方法名,topics[1]是发送者(from) ,topics[2]是接收者(to) 

data是转账金额;logIndex是当前交易hash中的日志id,可以当做当前的唯一标识(是当前块还是当前hash,需要确认下,忘了...)

其他的自己琢磨吧

当前使用web3版本是 "web3": "^1.0.0-beta.55"

如果是java,php自己找找这个方法,应该都有

猜你喜欢

转载自blog.csdn.net/zgf1991/article/details/100706740
今日推荐