Comparing version 0.1.8 to 0.1.9
@@ -0,1 +1,5 @@ | ||
# 0.1.9 -- event and log decoding | ||
1. Event and log decoding methods | ||
# 0.1.7 -- handles empty address decode nicely | ||
@@ -2,0 +6,0 @@ |
@@ -20,3 +20,3 @@ 'use strict'; | ||
if (types.length !== values.length) { | ||
throw new Error('[ethjs-abi] while encoding params, types/values mismatch, types length ' + types.length + ' should be ' + values.length); | ||
throw new Error('[ethjs-abi] while encoding params, types/values mismatch, Your contract requires ' + types.length + ' types (arguments), and you passed in ' + values.length); | ||
} | ||
@@ -68,2 +68,4 @@ | ||
function decodeParams(names, types, data) { | ||
var useNumberedParams = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; | ||
// Names is optional, so shift over all the parameters if not provided | ||
@@ -90,3 +92,3 @@ if (arguments.length < 3) { | ||
} | ||
values[index] = result.value; | ||
if (useNumberedParams) values[index] = result.value; | ||
if (names[index]) { | ||
@@ -121,10 +123,59 @@ values[names[index]] = result.value; | ||
function eventSignature(eventObject) { | ||
var signature = eventObject.name + '(' + utils.getKeys(eventObject.inputs, 'type').join(',') + ')'; | ||
return '0x' + utils.keccak256(signature); | ||
} | ||
// decode method data bytecode, from method ABI object | ||
function decodeEvent(eventObject, data) { | ||
var inputNames = utils.getKeys(eventObject.inputs, 'name', true); | ||
var inputTypes = utils.getKeys(eventObject.inputs, 'type'); | ||
function decodeEvent(eventObject, data, topics) { | ||
var useNumberedParams = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; | ||
return decodeParams(inputNames, inputTypes, utils.hexOrBuffer(data)); | ||
var nonIndexed = eventObject.inputs.filter(function (input) { | ||
return !input.indexed; | ||
}); | ||
var nonIndexedNames = utils.getKeys(nonIndexed, 'name', true); | ||
var nonIndexedTypes = utils.getKeys(nonIndexed, 'type'); | ||
var event = decodeParams(nonIndexedNames, nonIndexedTypes, utils.hexOrBuffer(data), useNumberedParams); | ||
var topicOffset = eventObject.anonymous ? 0 : 1; | ||
eventObject.inputs.filter(function (input) { | ||
return input.indexed; | ||
}).map(function (input, i) { | ||
var topic = new Buffer(topics[i + topicOffset].slice(2), 'hex'); | ||
var coder = getParamCoder(input.type); | ||
event[input.name] = coder.decode(topic, 0).value; | ||
}); | ||
event._eventName = eventObject.name; | ||
return event; | ||
} | ||
// Decode a specific log item with a specific event abi | ||
function decodeLogItem(eventObject, log) { | ||
var useNumberedParams = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; | ||
if (eventObject && log.topics[0] === eventSignature(eventObject)) { | ||
return decodeEvent(eventObject, log.data, log.topics, useNumberedParams); | ||
} | ||
} | ||
// Create a decoder for all events defined in an abi. It returns a function which is called | ||
// on an array of log entries such as received from getLogs or getTransactionReceipt and parses | ||
// any matching log entries | ||
function logDecoder(abi) { | ||
var useNumberedParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
var eventMap = {}; | ||
abi.filter(function (item) { | ||
return item.type === 'event'; | ||
}).map(function (item) { | ||
eventMap[eventSignature(item)] = item; | ||
}); | ||
return function (logItems) { | ||
return logItems.map(function (log) { | ||
return decodeLogItem(eventMap[log.topics[0]], log, useNumberedParams); | ||
}).filter(function (i) { | ||
return i; | ||
}); | ||
}; | ||
} | ||
module.exports = { | ||
@@ -136,3 +187,6 @@ encodeParams: encodeParams, | ||
encodeEvent: encodeEvent, | ||
decodeEvent: decodeEvent | ||
decodeEvent: decodeEvent, | ||
decodeLogItem: decodeLogItem, | ||
logDecoder: logDecoder, | ||
eventSignature: eventSignature | ||
}; |
@@ -6,4 +6,6 @@ 'use strict'; | ||
var contracts = require('./contracts.json'); | ||
var BN = require('bn.js'); | ||
describe('test basic encoding and decoding functionality', function () { | ||
var interfaceABI = [{ 'constant': false, 'inputs': [{ 'name': '_value', 'type': 'uint256' }], 'name': 'set', 'outputs': [{ 'name': '', 'type': 'bool' }], 'payable': false, 'type': 'function' }, { 'constant': false, 'inputs': [], 'name': 'get', 'outputs': [{ 'name': 'storeValue', 'type': 'uint256' }], 'payable': false, 'type': 'function' }, { 'anonymous': false, 'inputs': [{ 'indexed': false, 'name': '_newValue', 'type': 'uint256' }, { 'indexed': false, 'name': '_sender', 'type': 'address' }], 'name': 'SetComplete', 'type': 'event' }]; // eslint-disable-line | ||
it('should encode and decode contract data nicely', function () { | ||
@@ -13,3 +15,2 @@ var BalanceClaimInterface = JSON.parse(contracts.BalanceClaim['interface']); | ||
assert.equal(encodeBalanceClaimMethod1, '0x30509bca'); | ||
var interfaceABI = [{ 'constant': false, 'inputs': [{ 'name': '_value', 'type': 'uint256' }], 'name': 'set', 'outputs': [{ 'name': '', 'type': 'bool' }], 'payable': false, 'type': 'function' }, { 'constant': false, 'inputs': [], 'name': 'get', 'outputs': [{ 'name': 'storeValue', 'type': 'uint256' }], 'payable': false, 'type': 'function' }, { 'anonymous': false, 'inputs': [{ 'indexed': false, 'name': '_newValue', 'type': 'uint256' }, { 'indexed': false, 'name': '_sender', 'type': 'address' }], 'name': 'SetComplete', 'type': 'event' }]; // eslint-disable-line | ||
@@ -23,6 +24,62 @@ var setMethodInputBytecode = abi.encodeMethod(interfaceABI[0], [24000]); | ||
abi.encodeEvent(interfaceABI[2], [24000, '0xca35b7d915458ef540ade6068dfe2f44e8fa733c']); | ||
abi.decodeEvent(interfaceABI[2], '0x0000000000000000000000000000000000000000000000000000000000000d7d000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c'); | ||
var event = abi.decodeEvent(interfaceABI[2], '0x0000000000000000000000000000000000000000000000000000000000000d7d000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c'); | ||
assert.deepEqual(event, { | ||
0: new BN(3453), | ||
1: '0xca35b7d915458ef540ade6068dfe2f44e8fa733c', | ||
_eventName: 'SetComplete', | ||
_newValue: new BN(3453), | ||
_sender: '0xca35b7d915458ef540ade6068dfe2f44e8fa733c' | ||
}); | ||
assert.equal(setMethodInputBytecode, '0x60fe47b10000000000000000000000000000000000000000000000000000000000005dc0'); | ||
}); | ||
it('should decode event from log', function () { | ||
var eventAbi = { | ||
anonymous: false, | ||
inputs: [{ indexed: true, name: 'userKey', type: 'address' }, { indexed: false, name: 'proxy', type: 'address' }, { indexed: false, name: 'controller', type: 'address' }, { indexed: false, name: 'recoveryKey', type: 'address' }], | ||
name: 'IdentityCreated', | ||
type: 'event' | ||
}; | ||
assert.equal(abi.eventSignature(eventAbi), '0xc36800ebd6079fdafc3a7100d0d1172815751804a6d1b7eb365b85f6c9c80e61'); | ||
var logs = [{ | ||
address: '0xadb4966858672ef5ed70894030526544f9a5acdd', | ||
topics: ['0xc36800ebd6079fdafc3a7100d0d1172815751804a6d1b7eb365b85f6c9c80e61', '0x00000000000000000000000050858f2c7873fac9398ed9c195d185089caa7967'], | ||
data: '0x0000000000000000000000000aa622ec7d114c8a18730a9a6147ffbded11cefa000000000000000000000000cec030978d9e5e8b4ad689b1f509f8e9617efbe300000000000000000000000041f50a40900dc9ac8a6d4cfb4fa5e05ed428de42', | ||
blockNumber: 516657, | ||
transactionIndex: 1, | ||
transactionHash: '0x94761202b5fdcf50dfa8cc07abcc10b58744b985470dce6ab97b315a03f7f185', | ||
blockHash: '0x0c8092332fea2f708bcc6e4c105b7a5dc2bdf920e36e7336749c62e307be04e9', | ||
logIndex: 0 | ||
}]; | ||
var decoded = abi.decodeEvent(eventAbi, logs[0].data, logs[0].topics); | ||
assert.deepEqual(decoded, { | ||
0: '0x0aa622ec7d114c8a18730a9a6147ffbded11cefa', | ||
1: '0xcec030978d9e5e8b4ad689b1f509f8e9617efbe3', | ||
2: '0x41f50a40900dc9ac8a6d4cfb4fa5e05ed428de42', | ||
_eventName: 'IdentityCreated', | ||
userKey: '0x50858f2c7873fac9398ed9c195d185089caa7967', | ||
proxy: '0x0aa622ec7d114c8a18730a9a6147ffbded11cefa', | ||
controller: '0xcec030978d9e5e8b4ad689b1f509f8e9617efbe3', | ||
recoveryKey: '0x41f50a40900dc9ac8a6d4cfb4fa5e05ed428de42' | ||
}); | ||
assert.deepEqual(abi.decodeLogItem(eventAbi, logs[0], false), { | ||
_eventName: 'IdentityCreated', | ||
userKey: '0x50858f2c7873fac9398ed9c195d185089caa7967', | ||
proxy: '0x0aa622ec7d114c8a18730a9a6147ffbded11cefa', | ||
controller: '0xcec030978d9e5e8b4ad689b1f509f8e9617efbe3', | ||
recoveryKey: '0x41f50a40900dc9ac8a6d4cfb4fa5e05ed428de42' | ||
}); | ||
var decode = abi.logDecoder([eventAbi].concat(interfaceABI), false); | ||
assert.deepEqual(decode(logs), [{ | ||
_eventName: 'IdentityCreated', | ||
userKey: '0x50858f2c7873fac9398ed9c195d185089caa7967', | ||
proxy: '0x0aa622ec7d114c8a18730a9a6147ffbded11cefa', | ||
controller: '0xcec030978d9e5e8b4ad689b1f509f8e9617efbe3', | ||
recoveryKey: '0x41f50a40900dc9ac8a6d4cfb4fa5e05ed428de42' | ||
}]); | ||
}); | ||
}); |
{ | ||
"name": "ethjs-abi", | ||
"version": "0.1.8", | ||
"version": "0.1.9", | ||
"description": "Just the Ethereum encoding and decoding methods from the ethers-io-wallet.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -18,3 +18,3 @@ /* eslint-disable */ | ||
if (types.length !== values.length) { | ||
throw new Error(`[ethjs-abi] while encoding params, types/values mismatch, types length ${types.length} should be ${values.length}`); | ||
throw new Error(`[ethjs-abi] while encoding params, types/values mismatch, Your contract requires ${types.length} types (arguments), and you passed in ${values.length}`); | ||
} | ||
@@ -63,3 +63,3 @@ | ||
// decode bytecode data from output names and types | ||
function decodeParams(names, types, data) { | ||
function decodeParams(names, types, data, useNumberedParams = true) { | ||
// Names is optional, so shift over all the parameters if not provided | ||
@@ -86,3 +86,3 @@ if (arguments.length < 3) { | ||
} | ||
values[index] = result.value; | ||
if (useNumberedParams) values[index] = result.value; | ||
if (names[index]) { values[names[index]] = result.value; } | ||
@@ -115,10 +115,44 @@ }); | ||
function eventSignature(eventObject) { | ||
const signature = `${eventObject.name}(${utils.getKeys(eventObject.inputs, 'type').join(',')})`; | ||
return `0x${utils.keccak256(signature)}`; | ||
} | ||
// decode method data bytecode, from method ABI object | ||
function decodeEvent(eventObject, data) { | ||
const inputNames = utils.getKeys(eventObject.inputs, 'name', true); | ||
const inputTypes = utils.getKeys(eventObject.inputs, 'type'); | ||
function decodeEvent(eventObject, data, topics, useNumberedParams = true) { | ||
const nonIndexed = eventObject.inputs.filter((input) => !input.indexed) | ||
const nonIndexedNames = utils.getKeys(nonIndexed, 'name', true); | ||
const nonIndexedTypes = utils.getKeys(nonIndexed, 'type'); | ||
const event = decodeParams(nonIndexedNames, nonIndexedTypes, utils.hexOrBuffer(data), useNumberedParams); | ||
const topicOffset = eventObject.anonymous ? 0 : 1; | ||
eventObject.inputs.filter((input) => input.indexed).map((input, i) => { | ||
const topic = new Buffer(topics[i + topicOffset].slice(2),'hex'); | ||
const coder = getParamCoder(input.type); | ||
event[input.name] = coder.decode(topic, 0).value; | ||
}) | ||
event._eventName = eventObject.name | ||
return event; | ||
} | ||
return decodeParams(inputNames, inputTypes, utils.hexOrBuffer(data)); | ||
// Decode a specific log item with a specific event abi | ||
function decodeLogItem(eventObject, log, useNumberedParams = true) { | ||
if (eventObject && log.topics[0] === eventSignature(eventObject)) { | ||
return decodeEvent(eventObject, log.data, log.topics, useNumberedParams) | ||
} | ||
} | ||
// Create a decoder for all events defined in an abi. It returns a function which is called | ||
// on an array of log entries such as received from getLogs or getTransactionReceipt and parses | ||
// any matching log entries | ||
function logDecoder(abi, useNumberedParams = true) { | ||
const eventMap = {} | ||
abi.filter(item => item.type === 'event').map(item => { | ||
eventMap[eventSignature(item)] = item | ||
}) | ||
return function(logItems) { | ||
return logItems.map(log => decodeLogItem(eventMap[log.topics[0]], log, useNumberedParams)).filter(i => i) | ||
} | ||
} | ||
module.exports = { | ||
@@ -131,2 +165,5 @@ encodeParams, | ||
decodeEvent, | ||
decodeLogItem, | ||
logDecoder, | ||
eventSignature | ||
}; |
const assert = require('chai').assert; | ||
const abi = require('../index.js'); | ||
const contracts = require('./contracts.json'); | ||
const BN = require('bn.js'); | ||
describe('test basic encoding and decoding functionality', () => { | ||
const interfaceABI = [ | ||
{'constant':false,'inputs':[{'name':'_value','type':'uint256'}],'name':'set','outputs':[{'name':'','type':'bool'}],'payable':false,'type':'function'}, {'constant':false,'inputs':[],'name':'get','outputs':[{'name':'storeValue','type':'uint256'}],'payable':false,'type':'function'}, {'anonymous':false,'inputs':[{'indexed':false,'name':'_newValue','type':'uint256'},{'indexed':false,'name':'_sender','type':'address'}],'name':'SetComplete','type':'event'}]; // eslint-disable-line | ||
it('should encode and decode contract data nicely', () => { | ||
@@ -10,3 +13,2 @@ const BalanceClaimInterface = JSON.parse(contracts.BalanceClaim.interface); | ||
assert.equal(encodeBalanceClaimMethod1, '0x30509bca'); | ||
const interfaceABI = [{'constant':false,'inputs':[{'name':'_value','type':'uint256'}],'name':'set','outputs':[{'name':'','type':'bool'}],'payable':false,'type':'function'},{'constant':false,'inputs':[],'name':'get','outputs':[{'name':'storeValue','type':'uint256'}],'payable':false,'type':'function'},{'anonymous':false,'inputs':[{'indexed':false,'name':'_newValue','type':'uint256'},{'indexed':false,'name':'_sender','type':'address'}],'name':'SetComplete','type':'event'}]; // eslint-disable-line | ||
@@ -20,6 +22,70 @@ const setMethodInputBytecode = abi.encodeMethod(interfaceABI[0], [24000]); | ||
abi.encodeEvent(interfaceABI[2], [24000, '0xca35b7d915458ef540ade6068dfe2f44e8fa733c']); | ||
abi.decodeEvent(interfaceABI[2], '0x0000000000000000000000000000000000000000000000000000000000000d7d000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c'); | ||
const event = abi.decodeEvent(interfaceABI[2], '0x0000000000000000000000000000000000000000000000000000000000000d7d000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c'); | ||
assert.deepEqual(event, { | ||
0: new BN(3453), | ||
1: '0xca35b7d915458ef540ade6068dfe2f44e8fa733c', | ||
_eventName: 'SetComplete', | ||
_newValue: new BN(3453), | ||
_sender: '0xca35b7d915458ef540ade6068dfe2f44e8fa733c', | ||
}); | ||
assert.equal(setMethodInputBytecode, '0x60fe47b10000000000000000000000000000000000000000000000000000000000005dc0'); | ||
}); | ||
it('should decode event from log', () => { | ||
const eventAbi = { | ||
anonymous: false, | ||
inputs: [ | ||
{ indexed: true, name: 'userKey', type: 'address' }, | ||
{ indexed: false, name: 'proxy', type: 'address' }, | ||
{ indexed: false, name: 'controller', type: 'address' }, | ||
{ indexed: false, name: 'recoveryKey', type: 'address' }, | ||
], | ||
name: 'IdentityCreated', | ||
type: 'event', | ||
}; | ||
assert.equal(abi.eventSignature(eventAbi), '0xc36800ebd6079fdafc3a7100d0d1172815751804a6d1b7eb365b85f6c9c80e61'); | ||
const logs = [{ | ||
address: '0xadb4966858672ef5ed70894030526544f9a5acdd', | ||
topics: [ | ||
'0xc36800ebd6079fdafc3a7100d0d1172815751804a6d1b7eb365b85f6c9c80e61', | ||
'0x00000000000000000000000050858f2c7873fac9398ed9c195d185089caa7967', | ||
], | ||
data: '0x0000000000000000000000000aa622ec7d114c8a18730a9a6147ffbded11cefa000000000000000000000000cec030978d9e5e8b4ad689b1f509f8e9617efbe300000000000000000000000041f50a40900dc9ac8a6d4cfb4fa5e05ed428de42', | ||
blockNumber: 516657, | ||
transactionIndex: 1, | ||
transactionHash: '0x94761202b5fdcf50dfa8cc07abcc10b58744b985470dce6ab97b315a03f7f185', | ||
blockHash: '0x0c8092332fea2f708bcc6e4c105b7a5dc2bdf920e36e7336749c62e307be04e9', | ||
logIndex: 0, | ||
}]; | ||
const decoded = abi.decodeEvent(eventAbi, logs[0].data, logs[0].topics); | ||
assert.deepEqual(decoded, { | ||
0: '0x0aa622ec7d114c8a18730a9a6147ffbded11cefa', | ||
1: '0xcec030978d9e5e8b4ad689b1f509f8e9617efbe3', | ||
2: '0x41f50a40900dc9ac8a6d4cfb4fa5e05ed428de42', | ||
_eventName: 'IdentityCreated', | ||
userKey: '0x50858f2c7873fac9398ed9c195d185089caa7967', | ||
proxy: '0x0aa622ec7d114c8a18730a9a6147ffbded11cefa', | ||
controller: '0xcec030978d9e5e8b4ad689b1f509f8e9617efbe3', | ||
recoveryKey: '0x41f50a40900dc9ac8a6d4cfb4fa5e05ed428de42', | ||
}); | ||
assert.deepEqual(abi.decodeLogItem(eventAbi, logs[0], false), { | ||
_eventName: 'IdentityCreated', | ||
userKey: '0x50858f2c7873fac9398ed9c195d185089caa7967', | ||
proxy: '0x0aa622ec7d114c8a18730a9a6147ffbded11cefa', | ||
controller: '0xcec030978d9e5e8b4ad689b1f509f8e9617efbe3', | ||
recoveryKey: '0x41f50a40900dc9ac8a6d4cfb4fa5e05ed428de42', | ||
}); | ||
const decode = abi.logDecoder([eventAbi].concat(interfaceABI), false); | ||
assert.deepEqual(decode(logs), [{ | ||
_eventName: 'IdentityCreated', | ||
userKey: '0x50858f2c7873fac9398ed9c195d185089caa7967', | ||
proxy: '0x0aa622ec7d114c8a18730a9a6147ffbded11cefa', | ||
controller: '0xcec030978d9e5e8b4ad689b1f509f8e9617efbe3', | ||
recoveryKey: '0x41f50a40900dc9ac8a6d4cfb4fa5e05ed428de42', | ||
}]); | ||
}); | ||
}); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1927661
9669