diff --git a/index.js b/index.js index 1c5e5d1..071b9b0 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,11 @@ const { sha3, BN } = require("web3-utils"); const abiCoder = require("web3-eth-abi"); - +// topic id erc20 and erc721 +const transferSignature = "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; const state = { savedABIs: [], methodIDs: {}, + erc20IDs:{} }; function _getABIs() { @@ -32,7 +34,11 @@ function _addABI(abiArray) { ")" ); if (abi.type === "event") { - state.methodIDs[signature.slice(2)] = abi; + // add erc20 event in separate array + if ( state.methodIDs[signature.slice(2)] && signature.slice(2) === transferSignature) + state.erc20IDs[[signature.slice(2)]] = abi; + else + state.methodIDs[signature.slice(2)] = abi; } else { state.methodIDs[signature.slice(2, 10)] = abi; } @@ -131,12 +137,30 @@ function _decodeMethod(data) { } function _decodeLogs(logs) { - return logs.filter(log => log.topics.length > 0).map((logItem) => { - const methodID = logItem.topics[0].slice(2); - const method = state.methodIDs[methodID]; + const logsToProcess = logs.filter(log => log.topics.length > 0); + const resultSet = []; + for (let index = 0; index < logsToProcess.length; index++) { + let method; + const log = logsToProcess[index]; + const methodID = log.topics[0].slice(2); + // methodid mathches with erc20 transfer event and topic count length is 3 + // it means its erc20 transaction + if (methodID === transferSignature && log.topics.length<3){ + continue + } + else if (methodID === transferSignature && log.topics.length===3){ + method = state.erc20IDs[methodID]; + // if the dev have not supplied the erc721 abi, we will fetch erc20 method event info from methods + if (!method){ + method = state.methodIDs[methodID]; + } + } + + else + method = state.methodIDs[methodID]; + let decodedParams = []; if (method) { - const logData = logItem.data; - let decodedParams = []; + const logData = log.data; let dataIndex = 0; let topicsIndex = 1; @@ -146,27 +170,26 @@ function _decodeLogs(logs) { dataTypes.push(input.type); } }); - const decodedData = abiCoder.decodeParameters( dataTypes, logData.slice(2) ); - + // Loop topic and data to get the params method.inputs.map(function(param) { let decodedP = { name: param.name, type: param.type, }; - + if (param.indexed) { - decodedP.value = logItem.topics[topicsIndex]; + decodedP.value = log.topics[topicsIndex]; topicsIndex++; } else { decodedP.value = decodedData[dataIndex]; dataIndex++; } - + if (param.type === "address") { decodedP.value = decodedP.value.toLowerCase(); // 42 because len(0x) + 40 @@ -177,11 +200,11 @@ function _decodeLogs(logs) { decodedP.value = temp.join(""); } } - + if ( param.type === "uint256" || - param.type === "uint8" || - param.type === "int" + param.type === "uint8" || + param.type === "int" ) { // ensure to remove leading 0x for hex numbers if (typeof decodedP.value === "string" && decodedP.value.startsWith("0x")) { @@ -189,19 +212,19 @@ function _decodeLogs(logs) { } else { decodedP.value = new BN(decodedP.value).toString(10); } - + } - + decodedParams.push(decodedP); }); - - return { + resultSet.push({ name: method.name, events: decodedParams, - address: logItem.address, - }; + address: log.address, + }); } - }); + } + return resultSet; } module.exports = { diff --git a/package.json b/package.json index 48f5cd0..29b4d86 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "main": "index.js", "scripts": { "pretest": "eslint --ignore-path .eslintignore .", + "lint": "./node_modules/.bin/eslint ./", + "lint:fix": "eslint ./ --fix", "test": "mocha test", "dist": "webpack" }, diff --git a/test/test.js b/test/test.js index 03104f0..897ddcd 100644 --- a/test/test.js +++ b/test/test.js @@ -170,4 +170,72 @@ describe("abi decoder", function () { expect(Object.keys(methods)).to.have.length(39); }); + it("decode erc20 logs", () => { + const testABI = [{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]; + abiDecoder.addABI(testABI); + const testLogs = [ + { + data: "0x00000000000000000000000000000000000000000000000c1b1c7851eb4743ed", + topics: [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000009963c5d9b0a02d6316e546677c73932b0a60df1f", + "0x000000000000000000000000fd79fa24d5a883e90ed60d1c080f49cba6c925da", + ], + address: "0x2E10348eE563dEc5FE483DE558D1946b7A3372c2" + } + ]; + const decodedLogs = abiDecoder.decodeLogs(testLogs); + expect(decodedLogs).to.be.an("array"); + expect(decodedLogs).to.have.length(1); + expect(decodedLogs[0].name).to.equal("Transfer"); + expect(decodedLogs[0].events).to.have.length(3); + expect(decodedLogs[0].address).to.equal("0x2E10348eE563dEc5FE483DE558D1946b7A3372c2"); + expect(decodedLogs[0].events[0].name).to.equal("from"); + expect(decodedLogs[0].events[0].type).to.equal("address"); + expect(decodedLogs[0].events[0].value).to.equal("0x9963c5d9b0a02d6316e546677c73932b0a60df1f"); + expect(decodedLogs[0].events[1].name).to.equal("to"); + expect(decodedLogs[0].events[1].type).to.equal("address"); + expect(decodedLogs[0].events[1].value).to.equal("0xfd79fa24d5a883e90ed60d1c080f49cba6c925da"); + expect(decodedLogs[0].events[2].type).to.equal("uint256"); + expect(decodedLogs[0].events[2].name).to.equal("value"); + expect(decodedLogs[0].events[2].value).to.equal("223314497516121572333"); + + }); + it("decode erc721 logs", () => { + const testABI = [{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{ + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }],"name":"Transfer","type":"event"}]; + abiDecoder.addABI(testABI); + const testLogs = [ + { + data: "0x00000000000000000000000000000000000000000000000c1b1c7851eb4743ed", + topics: [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000009963c5d9b0a02d6316e546677c73932b0a60df1f", + "0x000000000000000000000000fd79fa24d5a883e90ed60d1c080f49cba6c925da", + ], + address: "0x2E10348eE563dEc5FE483DE558D1946b7A3372c2" + } + ]; + const decodedLogs = abiDecoder.decodeLogs(testLogs); + expect(decodedLogs).to.be.an("array"); + expect(decodedLogs).to.have.length(1); + expect(decodedLogs[0].name).to.equal("Transfer"); + expect(decodedLogs[0].events).to.have.length(3); + expect(decodedLogs[0].address).to.equal("0x2E10348eE563dEc5FE483DE558D1946b7A3372c2"); + expect(decodedLogs[0].events[0].name).to.equal("from"); + expect(decodedLogs[0].events[0].type).to.equal("address"); + expect(decodedLogs[0].events[0].value).to.equal("0x9963c5d9b0a02d6316e546677c73932b0a60df1f"); + expect(decodedLogs[0].events[1].name).to.equal("to"); + expect(decodedLogs[0].events[1].type).to.equal("address"); + expect(decodedLogs[0].events[1].value).to.equal("0xfd79fa24d5a883e90ed60d1c080f49cba6c925da"); + expect(decodedLogs[0].events[2].type).to.equal("uint256"); + expect(decodedLogs[0].events[2].name).to.equal("value"); + expect(decodedLogs[0].events[2].value).to.equal("223314497516121572333"); + + }); + });