監控以太坊交易記錄,監控以太坊代幣交易;
如題: 如何監控以太坊交易記錄
一般都是監控代幣轉賬記錄;
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);
監控方式
- 遍歷塊信息,匹配to是否是某代幣,再匹配input或者log,是否是transfer/transferFrom
- 使用websocket方式鏈接節點,通過訂閱的方式
- 使用etherscan.io/apis
- 本文主要介紹的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 內容結構如何解析,可以先自己測試獲取成功,去看實際返回值分析;
該記錄不是我的...網上摳的~~~
說下我目前的瞭解:
返回的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自己找找這個方法,應該都有