-
Notifications
You must be signed in to change notification settings - Fork 206
/
index.js
103 lines (94 loc) · 3.27 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
let utils = require('ethereumjs-util');
let abi = require('ethereumjs-abi');
let _ = require('underscore');
function isArray(type) {
return type.lastIndexOf(']') === type.length - 1;
}
function getElementType(type) {
const i = type.lastIndexOf('[');
return type.substring(0, i);
}
function formatSingle(type, data) {
let decodedData;
if (isArray(type)) {
// TODO: handle each array appropriately
const elementType = getElementType(type);
decodedData = _.map(data, function(data) {
return formatSingle(elementType, data);
});
} else if (type.includes('bytes')) {
const dataBuffer = Buffer.from(data, 'utf8');
decodedData = dataBuffer.toString('hex');
} else {
decodedData = data.toString();
}
return decodedData;
}
/**
* Decodes constructor args.
*
* @param {Object} contractABI - ABI of contract whose args to decode
* @param {string} bytecode - Constructor args bytecode
* @returns {Object} decodedArgs - Object representing decoded args with name, type, and data fields
*/
function decodeConstructorArgs(contractABI, bytecode) {
const constructor = _.findWhere(contractABI, { 'type': 'constructor'});
const inputNames = _.pluck(constructor.inputs, 'name');
const inputTypes = _.pluck(constructor.inputs, 'type');
let decoded = abi.rawDecode(inputTypes, new Buffer(bytecode, 'hex'));
let decodedArgs = _.map(decoded, function(e, i) {
const data = formatSingle(inputTypes[i], e);
return { 'name': inputNames[i], 'type': inputTypes[i], 'data': data };
});
return decodedArgs;
}
/**
* Generates constructor args bytecode based on input data.
*
* @param {Object[]} inputs - Array of objects with name, and type fields
* @param {string} inputs[].name - Name of argument
* @param {string} inputs[].type - Type of argument
* @returns {string} bytecode - Constructor args bytecode
*/
function encodeConstructorArgs(inputs) {
const inputTypes = _.pluck(inputs, 'type')
const args = _.pluck(inputs, 'data')
const encoded = abi.rawEncode(inputTypes, args);
const bytecode = encoded.toString('hex');
return bytecode;
}
/**
* Decodes function args.
*
* @param {Object} contractABI - ABI of contract whose args to decode
* @param {string} bytecode - full call args bytecode
* @returns {Object} decodedArgs - Object representing decoded args with name, type, and data fields
*/
function decodeFunctionArgs(contractABI, bytecode) {
const argsBuffer = new Buffer(bytecode, 'hex');
const methodID = argsBuffer.slice(0, 4);
const argsData = argsBuffer.slice(4);
const func = _.find(contractABI, function(o) {
if (o.type === 'function') {
const inputTypes = _.pluck(o.inputs, 'type');
return methodID.equals(abi.methodID(o.name, inputTypes));
}
return false;
});
if (!func) {
return null;
}
const inputNames = _.pluck(func.inputs, 'name');
const inputTypes = _.pluck(func.inputs, 'type');
let decoded = abi.rawDecode(inputTypes, argsData);
let decodedArgs = _.map(decoded, function(e, i) {
const data = formatSingle(inputTypes[i], e);
return { 'name': inputNames[i], 'type': inputTypes[i], 'data': data };
});
return decodedArgs;
}
module.exports = {
decodeConstructorArgs: decodeConstructorArgs,
encodeConstructorArgs: encodeConstructorArgs,
decodeFunctionArgs: decodeFunctionArgs
};