|
|
@ -28,20 +28,8 @@ var types = require('./types'); |
|
|
|
var c = require('./const'); |
|
|
|
var f = require('./formatters'); |
|
|
|
|
|
|
|
/// Filters all function from input abi
|
|
|
|
/// @returns abi array with filtered objects of type 'function'
|
|
|
|
var filterFunctions = function (json) { |
|
|
|
return json.filter(function (current) { |
|
|
|
return current.type === 'function'; |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/// Filters all events form input abi
|
|
|
|
/// @returns abi array with filtered objects of type 'event'
|
|
|
|
var filterEvents = function (json) { |
|
|
|
return json.filter(function (current) { |
|
|
|
return current.type === 'event'; |
|
|
|
}); |
|
|
|
var displayTypeError = function (type) { |
|
|
|
console.error('parser does not support type: ' + type); |
|
|
|
}; |
|
|
|
|
|
|
|
/// This method should be called if we want to check if givent type is an array type
|
|
|
@ -60,31 +48,31 @@ var dynamicTypeBytes = function (type, value) { |
|
|
|
var inputTypes = types.inputTypes(); |
|
|
|
|
|
|
|
/// Formats input params to bytes
|
|
|
|
/// @param abi contract method
|
|
|
|
/// @param abi contract method inputs
|
|
|
|
/// @param array of params that will be formatted to bytes
|
|
|
|
/// @returns bytes representation of input params
|
|
|
|
var toAbiInput = function (method, params) { |
|
|
|
var formatInput = function (inputs, params) { |
|
|
|
var bytes = ""; |
|
|
|
var padding = c.ETH_PADDING * 2; |
|
|
|
|
|
|
|
/// first we iterate in search for dynamic
|
|
|
|
method.inputs.forEach(function (input, index) { |
|
|
|
inputs.forEach(function (input, index) { |
|
|
|
bytes += dynamicTypeBytes(input.type, params[index]); |
|
|
|
}); |
|
|
|
|
|
|
|
method.inputs.forEach(function (input, i) { |
|
|
|
inputs.forEach(function (input, i) { |
|
|
|
var typeMatch = false; |
|
|
|
for (var j = 0; j < inputTypes.length && !typeMatch; j++) { |
|
|
|
typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]); |
|
|
|
typeMatch = inputTypes[j].type(inputs[i].type, params[i]); |
|
|
|
} |
|
|
|
if (!typeMatch) { |
|
|
|
console.error('input parser does not support type: ' + method.inputs[i].type); |
|
|
|
displayTypeError(inputs[i].type); |
|
|
|
} |
|
|
|
|
|
|
|
var formatter = inputTypes[j - 1].format; |
|
|
|
var toAppend = ""; |
|
|
|
|
|
|
|
if (arrayType(method.inputs[i].type)) |
|
|
|
if (arrayType(inputs[i].type)) |
|
|
|
toAppend = params[i].reduce(function (acc, curr) { |
|
|
|
return acc + formatter(curr); |
|
|
|
}, ""); |
|
|
@ -105,34 +93,34 @@ var dynamicBytesLength = function (type) { |
|
|
|
var outputTypes = types.outputTypes(); |
|
|
|
|
|
|
|
/// Formats output bytes back to param list
|
|
|
|
/// @param contract abi method
|
|
|
|
/// @param contract abi method outputs
|
|
|
|
/// @param bytes representtion of output
|
|
|
|
/// @returns array of output params
|
|
|
|
var fromAbiOutput = function (method, output) { |
|
|
|
var formatOutput = function (outs, output) { |
|
|
|
|
|
|
|
output = output.slice(2); |
|
|
|
var result = []; |
|
|
|
var padding = c.ETH_PADDING * 2; |
|
|
|
|
|
|
|
var dynamicPartLength = method.outputs.reduce(function (acc, curr) { |
|
|
|
var dynamicPartLength = outs.reduce(function (acc, curr) { |
|
|
|
return acc + dynamicBytesLength(curr.type); |
|
|
|
}, 0); |
|
|
|
|
|
|
|
var dynamicPart = output.slice(0, dynamicPartLength); |
|
|
|
output = output.slice(dynamicPartLength); |
|
|
|
|
|
|
|
method.outputs.forEach(function (out, i) { |
|
|
|
outs.forEach(function (out, i) { |
|
|
|
var typeMatch = false; |
|
|
|
for (var j = 0; j < outputTypes.length && !typeMatch; j++) { |
|
|
|
typeMatch = outputTypes[j].type(method.outputs[i].type); |
|
|
|
typeMatch = outputTypes[j].type(outs[i].type); |
|
|
|
} |
|
|
|
|
|
|
|
if (!typeMatch) { |
|
|
|
console.error('output parser does not support type: ' + method.outputs[i].type); |
|
|
|
displayTypeError(outs[i].type); |
|
|
|
} |
|
|
|
|
|
|
|
var formatter = outputTypes[j - 1].format; |
|
|
|
if (arrayType(method.outputs[i].type)) { |
|
|
|
if (arrayType(outs[i].type)) { |
|
|
|
var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); |
|
|
|
dynamicPart = dynamicPart.slice(padding); |
|
|
|
var array = []; |
|
|
@ -142,7 +130,7 @@ var fromAbiOutput = function (method, output) { |
|
|
|
} |
|
|
|
result.push(array); |
|
|
|
} |
|
|
|
else if (types.prefixedType('string')(method.outputs[i].type)) { |
|
|
|
else if (types.prefixedType('string')(outs[i].type)) { |
|
|
|
dynamicPart = dynamicPart.slice(padding); |
|
|
|
result.push(formatter(output.slice(0, padding))); |
|
|
|
output = output.slice(padding); |
|
|
@ -155,31 +143,18 @@ var fromAbiOutput = function (method, output) { |
|
|
|
return result; |
|
|
|
}; |
|
|
|
|
|
|
|
/// @returns display name for method eg. multiply(uint256) -> multiply
|
|
|
|
var methodDisplayName = function (method) { |
|
|
|
var length = method.indexOf('('); |
|
|
|
return length !== -1 ? method.substr(0, length) : method; |
|
|
|
}; |
|
|
|
|
|
|
|
/// @returns overloaded part of method's name
|
|
|
|
var methodTypeName = function (method) { |
|
|
|
/// TODO: make it invulnerable
|
|
|
|
var length = method.indexOf('('); |
|
|
|
return length !== -1 ? method.substr(length + 1, method.length - 1 - (length + 1)) : ""; |
|
|
|
}; |
|
|
|
|
|
|
|
/// @param json abi for contract
|
|
|
|
/// @returns input parser object for given json abi
|
|
|
|
/// TODO: refactor creating the parser, do not double logic from contract
|
|
|
|
var inputParser = function (json) { |
|
|
|
var parser = {}; |
|
|
|
json.forEach(function (method) { |
|
|
|
var displayName = methodDisplayName(method.name); |
|
|
|
var typeName = methodTypeName(method.name); |
|
|
|
var displayName = utils.extractDisplayName(method.name); |
|
|
|
var typeName = utils.extractTypeName(method.name); |
|
|
|
|
|
|
|
var impl = function () { |
|
|
|
var params = Array.prototype.slice.call(arguments); |
|
|
|
return toAbiInput(method, params); |
|
|
|
return formatInput(method.inputs, params); |
|
|
|
}; |
|
|
|
|
|
|
|
if (parser[displayName] === undefined) { |
|
|
@ -198,11 +173,11 @@ var outputParser = function (json) { |
|
|
|
var parser = {}; |
|
|
|
json.forEach(function (method) { |
|
|
|
|
|
|
|
var displayName = methodDisplayName(method.name); |
|
|
|
var typeName = methodTypeName(method.name); |
|
|
|
var displayName = utils.extractDisplayName(method.name); |
|
|
|
var typeName = utils.extractTypeName(method.name); |
|
|
|
|
|
|
|
var impl = function (output) { |
|
|
|
return fromAbiOutput(method, output); |
|
|
|
return formatOutput(method.outputs, output); |
|
|
|
}; |
|
|
|
|
|
|
|
if (parser[displayName] === undefined) { |
|
|
@ -215,20 +190,18 @@ var outputParser = function (json) { |
|
|
|
return parser; |
|
|
|
}; |
|
|
|
|
|
|
|
/// @param method name for which we want to get method signature
|
|
|
|
/// @returns (promise) contract method signature for method with given name
|
|
|
|
var methodSignature = function (name) { |
|
|
|
return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_METHOD_SIGNATURE_LENGTH * 2); |
|
|
|
/// @param function/event name for which we want to get signature
|
|
|
|
/// @returns signature of function/event with given name
|
|
|
|
var signatureFromAscii = function (name) { |
|
|
|
return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2); |
|
|
|
}; |
|
|
|
|
|
|
|
module.exports = { |
|
|
|
inputParser: inputParser, |
|
|
|
outputParser: outputParser, |
|
|
|
methodSignature: methodSignature, |
|
|
|
methodDisplayName: methodDisplayName, |
|
|
|
methodTypeName: methodTypeName, |
|
|
|
filterFunctions: filterFunctions, |
|
|
|
filterEvents: filterEvents |
|
|
|
formatInput: formatInput, |
|
|
|
formatOutput: formatOutput, |
|
|
|
signatureFromAscii: signatureFromAscii |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -262,7 +235,7 @@ if ("build" !== 'build') {/* |
|
|
|
|
|
|
|
module.exports = { |
|
|
|
ETH_PADDING: 32, |
|
|
|
ETH_METHOD_SIGNATURE_LENGTH: 4, |
|
|
|
ETH_SIGNATURE_LENGTH: 4, |
|
|
|
ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN } |
|
|
|
}; |
|
|
|
|
|
|
@ -292,8 +265,18 @@ module.exports = { |
|
|
|
|
|
|
|
var web3 = require('./web3'); |
|
|
|
var abi = require('./abi'); |
|
|
|
var utils = require('./utils'); |
|
|
|
var eventImpl = require('./event'); |
|
|
|
|
|
|
|
var exportNatspecGlobals = function (vars) { |
|
|
|
// it's used byt natspec.js
|
|
|
|
// TODO: figure out better way to solve this
|
|
|
|
web3._currentContractAbi = vars.abi; |
|
|
|
web3._currentContractAddress = vars.address; |
|
|
|
web3._currentContractMethodName = vars.method; |
|
|
|
web3._currentContractMethodParams = vars.params; |
|
|
|
}; |
|
|
|
|
|
|
|
var addFunctionRelatedPropertiesToContract = function (contract) { |
|
|
|
|
|
|
|
contract.call = function (options) { |
|
|
@ -323,14 +306,14 @@ var addFunctionsToContract = function (contract, desc, address) { |
|
|
|
var outputParser = abi.outputParser(desc); |
|
|
|
|
|
|
|
// create contract functions
|
|
|
|
abi.filterFunctions(desc).forEach(function (method) { |
|
|
|
utils.filterFunctions(desc).forEach(function (method) { |
|
|
|
|
|
|
|
var displayName = abi.methodDisplayName(method.name); |
|
|
|
var typeName = abi.methodTypeName(method.name); |
|
|
|
var displayName = utils.extractDisplayName(method.name); |
|
|
|
var typeName = utils.extractTypeName(method.name); |
|
|
|
|
|
|
|
var impl = function () { |
|
|
|
var params = Array.prototype.slice.call(arguments); |
|
|
|
var signature = abi.methodSignature(method.name); |
|
|
|
var signature = abi.signatureFromAscii(method.name); |
|
|
|
var parsed = inputParser[displayName][typeName].apply(null, params); |
|
|
|
|
|
|
|
var options = contract._options || {}; |
|
|
@ -345,12 +328,13 @@ var addFunctionsToContract = function (contract, desc, address) { |
|
|
|
contract._isTransact = null; |
|
|
|
|
|
|
|
if (isTransact) { |
|
|
|
// it's used byt natspec.js
|
|
|
|
// TODO: figure out better way to solve this
|
|
|
|
web3._currentContractAbi = desc; |
|
|
|
web3._currentContractAddress = address; |
|
|
|
web3._currentContractMethodName = method.name; |
|
|
|
web3._currentContractMethodParams = params; |
|
|
|
|
|
|
|
exportNatspecGlobals({ |
|
|
|
abi: desc, |
|
|
|
address: address, |
|
|
|
method: method.name, |
|
|
|
params: params |
|
|
|
}); |
|
|
|
|
|
|
|
// transactions do not have any output, cause we do not know, when they will be processed
|
|
|
|
web3.eth.transact(options); |
|
|
@ -382,8 +366,8 @@ var addEventRelatedPropertiesToContract = function (contract, desc, address) { |
|
|
|
|
|
|
|
Object.defineProperty(contract, 'topic', { |
|
|
|
get: function() { |
|
|
|
return abi.filterEvents(desc).map(function (e) { |
|
|
|
return abi.methodSignature(e.name); |
|
|
|
return utils.filterEvents(desc).map(function (e) { |
|
|
|
return abi.signatureFromAscii(e.name); |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
@ -392,11 +376,11 @@ var addEventRelatedPropertiesToContract = function (contract, desc, address) { |
|
|
|
|
|
|
|
var addEventsToContract = function (contract, desc, address) { |
|
|
|
// create contract events
|
|
|
|
abi.filterEvents(desc).forEach(function (e) { |
|
|
|
utils.filterEvents(desc).forEach(function (e) { |
|
|
|
|
|
|
|
var impl = function () { |
|
|
|
var params = Array.prototype.slice.call(arguments); |
|
|
|
var signature = abi.methodSignature(e.name); |
|
|
|
var signature = abi.signatureFromAscii(e.name); |
|
|
|
var event = eventImpl(address, signature, e); |
|
|
|
var o = event.apply(null, params); |
|
|
|
return web3.eth.watch(o); |
|
|
@ -405,18 +389,8 @@ var addEventsToContract = function (contract, desc, address) { |
|
|
|
// this property should be used by eth.filter to check if object is an event
|
|
|
|
impl._isEvent = true; |
|
|
|
|
|
|
|
// TODO: we can remove address && topic properties, they are not used anymore since we introduced _isEvent
|
|
|
|
impl.address = address; |
|
|
|
|
|
|
|
Object.defineProperty(impl, 'topic', { |
|
|
|
get: function() { |
|
|
|
return [abi.methodSignature(e.name)]; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// TODO: rename these methods, cause they are used not only for methods
|
|
|
|
var displayName = abi.methodDisplayName(e.name); |
|
|
|
var typeName = abi.methodTypeName(e.name); |
|
|
|
var displayName = utils.extractDisplayName(e.name); |
|
|
|
var typeName = utils.extractTypeName(e.name); |
|
|
|
|
|
|
|
if (contract[displayName] === undefined) { |
|
|
|
contract[displayName] = impl; |
|
|
@ -476,7 +450,7 @@ var contract = function (address, desc) { |
|
|
|
module.exports = contract; |
|
|
|
|
|
|
|
|
|
|
|
},{"./abi":1,"./event":4,"./web3":12}],4:[function(require,module,exports){ |
|
|
|
},{"./abi":1,"./event":4,"./utils":11,"./web3":12}],4:[function(require,module,exports){ |
|
|
|
/* |
|
|
|
This file is part of ethereum.js. |
|
|
|
|
|
|
@ -508,7 +482,7 @@ var inputWithName = function (inputs, name) { |
|
|
|
}); |
|
|
|
|
|
|
|
if (index === -1) { |
|
|
|
console.error('indexed paray with name ' + name + ' not found'); |
|
|
|
console.error('indexed param with name ' + name + ' not found'); |
|
|
|
return undefined; |
|
|
|
} |
|
|
|
return inputs[index]; |
|
|
@ -517,19 +491,15 @@ var inputWithName = function (inputs, name) { |
|
|
|
var indexedParamsToTopics = function (event, indexed) { |
|
|
|
// sort keys?
|
|
|
|
return Object.keys(indexed).map(function (key) { |
|
|
|
// TODO: simplify this!
|
|
|
|
var parser = abi.inputParser([{ |
|
|
|
name: 'test', |
|
|
|
inputs: [inputWithName(event.inputs, key)] |
|
|
|
}]); |
|
|
|
var inputs = [inputWithName(event.inputs, key)]; |
|
|
|
|
|
|
|
var value = indexed[key]; |
|
|
|
if (value instanceof Array) { |
|
|
|
return value.map(function (v) { |
|
|
|
return parser.test(v); |
|
|
|
return abi.formatInput(inputs, [v]); |
|
|
|
}); |
|
|
|
} |
|
|
|
return parser.test(value); |
|
|
|
return abi.formatInput(inputs, [value]); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
@ -1178,11 +1148,43 @@ var fromAscii = function(str, pad) { |
|
|
|
return "0x" + hex; |
|
|
|
}; |
|
|
|
|
|
|
|
/// @returns display name for function/event eg. multiply(uint256) -> multiply
|
|
|
|
var extractDisplayName = function (name) { |
|
|
|
var length = name.indexOf('('); |
|
|
|
return length !== -1 ? name.substr(0, length) : name; |
|
|
|
}; |
|
|
|
|
|
|
|
/// @returns overloaded part of function/event name
|
|
|
|
var extractTypeName = function (name) { |
|
|
|
/// TODO: make it invulnerable
|
|
|
|
var length = name.indexOf('('); |
|
|
|
return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)) : ""; |
|
|
|
}; |
|
|
|
|
|
|
|
/// Filters all function from input abi
|
|
|
|
/// @returns abi array with filtered objects of type 'function'
|
|
|
|
var filterFunctions = function (json) { |
|
|
|
return json.filter(function (current) { |
|
|
|
return current.type === 'function'; |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/// Filters all events form input abi
|
|
|
|
/// @returns abi array with filtered objects of type 'event'
|
|
|
|
var filterEvents = function (json) { |
|
|
|
return json.filter(function (current) { |
|
|
|
return current.type === 'event'; |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
module.exports = { |
|
|
|
findIndex: findIndex, |
|
|
|
toAscii: toAscii, |
|
|
|
fromAscii: fromAscii |
|
|
|
fromAscii: fromAscii, |
|
|
|
extractDisplayName: extractDisplayName, |
|
|
|
extractTypeName: extractTypeName, |
|
|
|
filterFunctions: filterFunctions, |
|
|
|
filterEvents: filterEvents |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|