diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html index 509a9aeeb3..115145c4c4 100644 --- a/cmd/mist/assets/examples/coin.html +++ b/cmd/mist/assets/examples/coin.html @@ -35,7 +35,7 @@ var web3 = require('web3'); var eth = web3.eth; - web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8545')); + web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545')); var desc = [{ "name": "balance(address)", "type": "function", diff --git a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js index 5b7d872706..7b2531677a 100644 --- a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js +++ b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js @@ -22,34 +22,57 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof requ * @date 2014 */ -var utils = require('./utils'); +var utils = require('../utils/utils'); +var c = require('../utils/config'); var types = require('./types'); -var c = require('./const'); var f = require('./formatters'); -var displayTypeError = function (type) { - console.error('parser does not support type: ' + type); +/** + * throw incorrect type error + * + * @method throwTypeError + * @param {String} type + * @throws incorrect type error + */ +var throwTypeError = function (type) { + throw new Error('parser does not support type: ' + type); }; -/// This method should be called if we want to check if givent type is an array type -/// @returns true if it is, otherwise false -var arrayType = function (type) { +/** This method should be called if we want to check if givent type is an array type + * + * @method isArrayType + * @param {String} type name + * @returns {Boolean} true if it is, otherwise false + */ +var isArrayType = function (type) { return type.slice(-2) === '[]'; }; +/** + * This method should be called to return dynamic type length in hex + * + * @method dynamicTypeBytes + * @param {String} type + * @param {String|Array} dynamic type + * @return {String} length of dynamic type in hex or empty string if type is not dynamic + */ var dynamicTypeBytes = function (type, value) { // TODO: decide what to do with array of strings - if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + if (isArrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. return f.formatInputInt(value.length); return ""; }; var inputTypes = types.inputTypes(); -/// Formats input params to bytes -/// @param abi contract method inputs -/// @param array of params that will be formatted to bytes -/// @returns bytes representation of input params +/** + * Formats input params to bytes + * + * @method formatInput + * @param {Array} abi inputs of method + * @param {Array} params that will be formatted to bytes + * @returns bytes representation of input params + */ var formatInput = function (inputs, params) { var bytes = ""; var toAppendConstant = ""; @@ -67,12 +90,12 @@ var formatInput = function (inputs, params) { typeMatch = inputTypes[j].type(inputs[i].type, params[i]); } if (!typeMatch) { - displayTypeError(inputs[i].type); + throwTypeError(inputs[i].type); } var formatter = inputTypes[j - 1].format; - if (arrayType(inputs[i].type)) + if (isArrayType(inputs[i].type)) toAppendArrayContent += params[i].reduce(function (acc, curr) { return acc + formatter(curr); }, ""); @@ -87,18 +110,29 @@ var formatInput = function (inputs, params) { return bytes; }; +/** + * This method should be called to predict the length of dynamic type + * + * @method dynamicBytesLength + * @param {String} type + * @returns {Number} length of dynamic type, 0 or multiplication of ETH_PADDING (32) + */ var dynamicBytesLength = function (type) { - if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + if (isArrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. return c.ETH_PADDING * 2; return 0; }; var outputTypes = types.outputTypes(); -/// Formats output bytes back to param list -/// @param contract abi method outputs -/// @param bytes representtion of output -/// @returns array of output params +/** + * Formats output bytes back to param list + * + * @method formatOutput + * @param {Array} abi outputs of method + * @param {String} bytes represention of output + * @returns {Array} output params + */ var formatOutput = function (outs, output) { output = output.slice(2); @@ -120,11 +154,11 @@ var formatOutput = function (outs, output) { } if (!typeMatch) { - displayTypeError(outs[i].type); + throwTypeError(outs[i].type); } var formatter = outputTypes[j - 1].format; - if (arrayType(outs[i].type)) { + if (isArrayType(outs[i].type)) { var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); dynamicPart = dynamicPart.slice(padding); var array = []; @@ -147,9 +181,14 @@ var formatOutput = function (outs, output) { return result; }; -/// @param json abi for contract -/// @returns input parser object for given json abi -/// TODO: refactor creating the parser, do not double logic from contract +/** + * Should be called to create input parser for contract with given abi + * + * @method inputParser + * @param {Array} contract abi + * @returns {Object} 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) { @@ -171,8 +210,13 @@ var inputParser = function (json) { return parser; }; -/// @param json abi for contract -/// @returns output parser for given json abi +/** + * Should be called to create output parser for contract with given abi + * + * @method outputParser + * @param {Array} contract abi + * @returns {Object} output parser for given json abi + */ var outputParser = function (json) { var parser = {}; json.forEach(function (method) { @@ -201,7 +245,7 @@ module.exports = { formatOutput: formatOutput }; -},{"./const":2,"./formatters":8,"./types":15,"./utils":16}],2:[function(require,module,exports){ +},{"../utils/config":4,"../utils/utils":5,"./formatters":2,"./types":3}],2:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -218,49 +262,206 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file const.js +/** @file formatters.js * @authors: * Marek Kotewicz * @date 2015 */ -/// required to define ETH_BIGNUMBER_ROUNDING_MODE if ("build" !== 'build') {/* var BigNumber = require('bignumber.js'); // jshint ignore:line */} -var ETH_UNITS = [ - 'wei', - 'Kwei', - 'Mwei', - 'Gwei', - 'szabo', - 'finney', - 'ether', - 'grand', - 'Mether', - 'Gether', - 'Tether', - 'Pether', - 'Eether', - 'Zether', - 'Yether', - 'Nether', - 'Dether', - 'Vether', - 'Uether' -]; +var utils = require('../utils/utils'); +var c = require('../utils/config'); + +/** + * Should be called to pad string to expected length + * + * @method padLeft + * @param {String} string to be padded + * @param {Number} characters that result string should have + * @param {String} sign, by default 0 + * @returns {String} right aligned string + */ +var padLeft = function (string, chars, sign) { + return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; +}; + +/** + * Formats input value to byte representation of int + * If value is negative, return it's two's complement + * If the value is floating point, round it down + * + * @method formatInputInt + * @param {String|Number|BigNumber} value that needs to be formatted + * @returns {String} right-aligned byte representation of int + */ +var formatInputInt = function (value) { + var padding = c.ETH_PADDING * 2; + BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); + return padLeft(utils.toTwosComplement(value).round().toString(16), padding); +}; + +/** + * Formats input value to byte representation of string + * + * @method formatInputString + * @param {String} + * @returns {String} left-algined byte representation of string + */ +var formatInputString = function (value) { + return utils.fromAscii(value, c.ETH_PADDING).substr(2); +}; + +/** + * Formats input value to byte representation of bool + * + * @method formatInputBool + * @param {Boolean} + * @returns {String} right-aligned byte representation bool + */ +var formatInputBool = function (value) { + return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); +}; + +/** + * Formats input value to byte representation of real + * Values are multiplied by 2^m and encoded as integers + * + * @method formatInputReal + * @param {String|Number|BigNumber} + * @returns {String} byte representation of real + */ +var formatInputReal = function (value) { + return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); +}; + +/** + * Check if input value is negative + * + * @method signedIsNegative + * @param {String} value is hex format + * @returns {Boolean} true if it is negative, otherwise false + */ +var signedIsNegative = function (value) { + return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; +}; + +/** + * Formats right-aligned output bytes to int + * + * @method formatOutputInt + * @param {String} bytes + * @returns {BigNumber} right-aligned output bytes formatted to big number + */ +var formatOutputInt = function (value) { + + value = value || "0"; + + // check if it's negative number + // it it is, return two's complement + if (signedIsNegative(value)) { + return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); + } + return new BigNumber(value, 16); +}; + +/** + * Formats right-aligned output bytes to uint + * + * @method formatOutputUInt + * @param {String} bytes + * @returns {BigNumeber} right-aligned output bytes formatted to uint + */ +var formatOutputUInt = function (value) { + value = value || "0"; + return new BigNumber(value, 16); +}; + +/** + * Formats right-aligned output bytes to real + * + * @method formatOutputReal + * @param {String} + * @returns {BigNumber} input bytes formatted to real + */ +var formatOutputReal = function (value) { + return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/** + * Formats right-aligned output bytes to ureal + * + * @method formatOutputUReal + * @param {String} + * @returns {BigNumber} input bytes formatted to ureal + */ +var formatOutputUReal = function (value) { + return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/** + * Should be used to format output hash + * + * @method formatOutputHash + * @param {String} + * @returns {String} right-aligned output bytes formatted to hex + */ +var formatOutputHash = function (value) { + return "0x" + value; +}; + +/** + * Should be used to format output bool + * + * @method formatOutputBool + * @param {String} + * @returns {Boolean} right-aligned input bytes formatted to bool + */ +var formatOutputBool = function (value) { + return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; +}; + +/** + * Should be used to format output string + * + * @method formatOutputString + * @param {Sttring} left-aligned hex representation of string + * @returns {String} ascii string + */ +var formatOutputString = function (value) { + return utils.toAscii(value); +}; + +/** + * Should be used to format output address + * + * @method formatOutputAddress + * @param {String} right-aligned input bytes + * @returns {String} address + */ +var formatOutputAddress = function (value) { + return "0x" + value.slice(value.length - 40, value.length); +}; module.exports = { - ETH_PADDING: 32, - ETH_SIGNATURE_LENGTH: 4, - ETH_UNITS: ETH_UNITS, - ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, - ETH_POLLING_TIMEOUT: 1000 + formatInputInt: formatInputInt, + formatInputString: formatInputString, + formatInputBool: formatInputBool, + formatInputReal: formatInputReal, + formatOutputInt: formatOutputInt, + formatOutputUInt: formatOutputUInt, + formatOutputReal: formatOutputReal, + formatOutputUReal: formatOutputUReal, + formatOutputHash: formatOutputHash, + formatOutputBool: formatOutputBool, + formatOutputString: formatOutputString, + formatOutputAddress: formatOutputAddress }; -},{}],3:[function(require,module,exports){ +},{"../utils/config":4,"../utils/utils":5}],3:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -277,235 +478,71 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file contract.js +/** @file types.js * @authors: * Marek Kotewicz - * @date 2014 + * @date 2015 */ -var web3 = require('./web3'); -var abi = require('./abi'); -var utils = require('./utils'); -var eventImpl = require('./event'); -var signature = require('./signature'); - -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 f = require('./formatters'); -var addFunctionRelatedPropertiesToContract = function (contract) { - - contract.call = function (options) { - contract._isTransaction = false; - contract._options = options; - return contract; +/// @param expected type prefix (string) +/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false +var prefixedType = function (prefix) { + return function (type) { + return type.indexOf(prefix) === 0; }; +}; - - contract.sendTransaction = function (options) { - contract._isTransaction = true; - contract._options = options; - return contract; +/// @param expected type name (string) +/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false +var namedType = function (name) { + return function (type) { + return name === type; }; - // DEPRECATED - contract.transact = function (options) { +}; - console.warn('myContract.transact() is deprecated please use myContract.sendTransaction() instead.'); +/// Setups input formatters for solidity types +/// @returns an array of input formatters +var inputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatInputInt }, + { type: prefixedType('int'), format: f.formatInputInt }, + { type: prefixedType('hash'), format: f.formatInputInt }, + { type: prefixedType('string'), format: f.formatInputString }, + { type: prefixedType('real'), format: f.formatInputReal }, + { type: prefixedType('ureal'), format: f.formatInputReal }, + { type: namedType('address'), format: f.formatInputInt }, + { type: namedType('bool'), format: f.formatInputBool } + ]; +}; - return contract.sendTransaction(options); - }; +/// Setups output formaters for solidity types +/// @returns an array of output formatters +var outputTypes = function () { - contract._options = {}; - ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) { - contract[p] = function (v) { - contract._options[p] = v; - return contract; - }; - }); + return [ + { type: prefixedType('uint'), format: f.formatOutputUInt }, + { type: prefixedType('int'), format: f.formatOutputInt }, + { type: prefixedType('hash'), format: f.formatOutputHash }, + { type: prefixedType('string'), format: f.formatOutputString }, + { type: prefixedType('real'), format: f.formatOutputReal }, + { type: prefixedType('ureal'), format: f.formatOutputUReal }, + { type: namedType('address'), format: f.formatOutputAddress }, + { type: namedType('bool'), format: f.formatOutputBool } + ]; +}; +module.exports = { + prefixedType: prefixedType, + namedType: namedType, + inputTypes: inputTypes, + outputTypes: outputTypes }; -var addFunctionsToContract = function (contract, desc, address) { - var inputParser = abi.inputParser(desc); - var outputParser = abi.outputParser(desc); - // create contract functions - utils.filterFunctions(desc).forEach(function (method) { - - var displayName = utils.extractDisplayName(method.name); - var typeName = utils.extractTypeName(method.name); - - var impl = function () { - /*jshint maxcomplexity:7 */ - var params = Array.prototype.slice.call(arguments); - var sign = signature.functionSignatureFromAscii(method.name); - var parsed = inputParser[displayName][typeName].apply(null, params); - - var options = contract._options || {}; - options.to = address; - options.data = sign + parsed; - - var isTransaction = contract._isTransaction === true || (contract._isTransaction !== false && !method.constant); - var collapse = options.collapse !== false; - - // reset - contract._options = {}; - contract._isTransaction = null; - - if (isTransaction) { - - 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.sendTransaction(options); - return; - } - - var output = web3.eth.call(options); - var ret = outputParser[displayName][typeName](output); - if (collapse) - { - if (ret.length === 1) - ret = ret[0]; - else if (ret.length === 0) - ret = null; - } - return ret; - }; - - if (contract[displayName] === undefined) { - contract[displayName] = impl; - } - - contract[displayName][typeName] = impl; - }); -}; - -var addEventRelatedPropertiesToContract = function (contract, desc, address) { - contract.address = address; - contract._onWatchEventResult = function (data) { - var matchingEvent = event.getMatchingEvent(utils.filterEvents(desc)); - var parser = eventImpl.outputParser(matchingEvent); - return parser(data); - }; - - Object.defineProperty(contract, 'topic', { - get: function() { - return utils.filterEvents(desc).map(function (e) { - return signature.eventSignatureFromAscii(e.name); - }); - } - }); - -}; - -var addEventsToContract = function (contract, desc, address) { - // create contract events - utils.filterEvents(desc).forEach(function (e) { - - var impl = function () { - var params = Array.prototype.slice.call(arguments); - var sign = signature.eventSignatureFromAscii(e.name); - var event = eventImpl.inputParser(address, sign, e); - var o = event.apply(null, params); - var outputFormatter = function (data) { - var parser = eventImpl.outputParser(e); - return parser(data); - }; - return web3.eth.filter(o, undefined, undefined, outputFormatter); - }; - - // this property should be used by eth.filter to check if object is an event - impl._isEvent = true; - - var displayName = utils.extractDisplayName(e.name); - var typeName = utils.extractTypeName(e.name); - - if (contract[displayName] === undefined) { - contract[displayName] = impl; - } - - contract[displayName][typeName] = impl; - - }); -}; - - -/** - * This method should be called when we want to call / transact some solidity method from javascript - * it returns an object which has same methods available as solidity contract description - * usage example: - * - * var abi = [{ - * name: 'myMethod', - * inputs: [{ name: 'a', type: 'string' }], - * outputs: [{name: 'd', type: 'string' }] - * }]; // contract abi - * - * var MyContract = web3.eth.contract(abi); // creation of contract prototype - * - * var contractInstance = new MyContract('0x0123123121'); - * - * contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default) - * contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit) - * contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction - * - * @param abi - abi json description of the contract, which is being created - * @returns contract object - */ -var contract = function (abi) { - - // return prototype - if(abi instanceof Array && arguments.length === 1) { - return Contract.bind(null, abi); - - // deprecated: auto initiate contract - } else { - - console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); - - return new Contract(arguments[1], arguments[0]); - } - -}; - -function Contract(abi, address) { - - // workaround for invalid assumption that method.name is the full anonymous prototype of the method. - // it's not. it's just the name. the rest of the code assumes it's actually the anonymous - // prototype, so we make it so as a workaround. - // TODO: we may not want to modify input params, maybe use copy instead? - abi.forEach(function (method) { - if (method.name.indexOf('(') === -1) { - var displayName = method.name; - var typeName = method.inputs.map(function(i){return i.type; }).join(); - method.name = displayName + '(' + typeName + ')'; - } - }); - - var result = {}; - addFunctionRelatedPropertiesToContract(result); - addFunctionsToContract(result, abi, address); - addEventRelatedPropertiesToContract(result, abi, address); - addEventsToContract(result, abi, address); - - return result; -} - -module.exports = contract; - - -},{"./abi":1,"./event":6,"./signature":14,"./utils":16,"./web3":18}],4:[function(require,module,exports){ +},{"./formatters":2}],4:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -522,26 +559,62 @@ module.exports = contract; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file db.js +/** @file config.js * @authors: * Marek Kotewicz * @date 2015 */ -/// @returns an array of objects describing web3.db api methods -var methods = function () { - return [ - { name: 'put', call: 'db_put' }, - { name: 'get', call: 'db_get' }, - { name: 'putString', call: 'db_putString' }, - { name: 'getString', call: 'db_getString' } - ]; -}; +/** + * Utils + * + * @module utils + */ + +/** + * Utility functions + * + * @class [utils] config + * @constructor + */ + +/// required to define ETH_BIGNUMBER_ROUNDING_MODE +if ("build" !== 'build') {/* + var BigNumber = require('bignumber.js'); // jshint ignore:line +*/} + +var ETH_UNITS = [ + 'wei', + 'Kwei', + 'Mwei', + 'Gwei', + 'szabo', + 'finney', + 'ether', + 'grand', + 'Mether', + 'Gether', + 'Tether', + 'Pether', + 'Eether', + 'Zether', + 'Yether', + 'Nether', + 'Dether', + 'Vether', + 'Uether' +]; module.exports = { - methods: methods + ETH_PADDING: 32, + ETH_SIGNATURE_LENGTH: 4, + ETH_UNITS: ETH_UNITS, + ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, + ETH_POLLING_TIMEOUT: 1000, + ETH_DEFAULTBLOCK: 'latest' }; + },{}],5:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -559,237 +632,409 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file eth.js +/** @file utils.js * @authors: * Marek Kotewicz * @date 2015 */ -var formatters = require('./formatters'); - - -var blockCall = function (args) { - return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; -}; +/** + * Utils + * + * @module utils + */ -var transactionCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; -}; +/** + * Utility functions + * + * @class [utils] utils + * @constructor + */ -var uncleCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; -}; +if ("build" !== 'build') {/* + var BigNumber = require('bignumber.js'); // jshint ignore:line +*/} -var transactionCountCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionCountByHash' : 'eth_transactionCountByNumber'; +var unitMap = { + 'wei': '1', + 'kwei': '1000', + 'ada': '1000', + 'mwei': '1000000', + 'babbage': '1000000', + 'gwei': '1000000000', + 'shannon': '1000000000', + 'szabo': '1000000000000', + 'finney': '1000000000000000', + 'ether': '1000000000000000000', + 'kether': '1000000000000000000000', + 'grand': '1000000000000000000000', + 'einstein': '1000000000000000000000', + 'mether': '1000000000000000000000000', + 'gether': '1000000000000000000000000000', + 'tether': '1000000000000000000000000000000' }; -var uncleCountCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleCountByHash' : 'eth_uncleCountByNumber'; -}; -/// @returns an array of objects describing web3.eth api methods -var methods = [ - { name: 'getBalance', call: 'eth_balanceAt', outputFormatter: formatters.convertToBigNumber}, - { name: 'getState', call: 'eth_stateAt' }, - { name: 'getStorage', call: 'eth_storageAt' }, - { name: 'getData', call: 'eth_codeAt' }, - { name: 'getBlock', call: blockCall, outputFormatter: formatters.outputBlockFormatter}, - { name: 'getUncle', call: uncleCall, outputFormatter: formatters.outputBlockFormatter}, - { name: 'getCompilers', call: 'eth_compilers' }, - { name: 'getBlockTransactionCount', call: transactionCountCall }, - { name: 'getBlockUncleCount', call: uncleCountCall }, - { name: 'getTransaction', call: transactionCall, outputFormatter: formatters.outputTransactionFormatter }, - { name: 'getTransactionCount', call: 'eth_countAt'}, - { name: 'sendTransaction', call: 'eth_transact', inputFormatter: formatters.inputTransactionFormatter }, - { name: 'call', call: 'eth_call' }, - { name: 'compile.solidity', call: 'eth_solidity' }, - { name: 'compile.lll', call: 'eth_lll' }, - { name: 'compile.serpent', call: 'eth_serpent' }, - { name: 'flush', call: 'eth_flush' }, +/** Finds first index of array element matching pattern + * + * @method findIndex + * @param {Array} + * @param {Function} pattern + * @returns {Number} index of element + */ +var findIndex = function (array, callback) { + var end = false; + var i = 0; + for (; i < array.length && !end; i++) { + end = callback(array[i]); + } + return end ? i - 1 : -1; +}; - // deprecated methods - { name: 'balanceAt', call: 'eth_balanceAt', newMethod: 'getBalance' }, - { name: 'stateAt', call: 'eth_stateAt', newMethod: 'getState' }, - { name: 'storageAt', call: 'eth_storageAt', newMethod: 'getStorage' }, - { name: 'countAt', call: 'eth_countAt', newMethod: 'getTransactionCount' }, - { name: 'codeAt', call: 'eth_codeAt', newMethod: 'getData' }, - { name: 'transact', call: 'eth_transact', newMethod: 'sendTransaction' }, - { name: 'block', call: blockCall, newMethod: 'getBlock' }, - { name: 'transaction', call: transactionCall, newMethod: 'getTransaction' }, - { name: 'uncle', call: uncleCall, newMethod: 'getUncle' }, - { name: 'compilers', call: 'eth_compilers', newMethod: 'getCompilers' }, - { name: 'solidity', call: 'eth_solidity', newMethod: 'compile.solidity' }, - { name: 'lll', call: 'eth_lll', newMethod: 'compile.lll' }, - { name: 'serpent', call: 'eth_serpent', newMethod: 'compile.serpent' }, - { name: 'transactionCount', call: transactionCountCall, newMethod: 'getBlockTransactionCount' }, - { name: 'uncleCount', call: uncleCountCall, newMethod: 'getBlockUncleCount' }, - { name: 'logs', call: 'eth_logs' } -]; +/** + * Should be called to get sting from it's hex representation + * + * @method toAscii + * @param {String} string in hex + * @returns {String} ascii string representation of hex value + */ +var toAscii = function(hex) { +// Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') { + i = 2; + } + for (; i < l; i+=2) { + var code = parseInt(hex.substr(i, 2), 16); + if (code === 0) { + break; + } -/// @returns an array of objects describing web3.eth api properties -var properties = [ - { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, - { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, - { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, - { name: 'gasPrice', getter: 'eth_gasPrice', outputFormatter: formatters.convertToBigNumber}, - { name: 'accounts', getter: 'eth_accounts' }, - { name: 'peerCount', getter: 'eth_peerCount' }, - { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, - { name: 'blockNumber', getter: 'eth_number'}, + str += String.fromCharCode(code); + } - // deprecated properties - { name: 'number', getter: 'eth_number', newProperty: 'blockNumber'} -]; + return str; +}; + +/** + * Shold be called to get hex representation (prefixed by 0x) of ascii string + * + * @method fromAscii + * @param {String} string + * @returns {String} hex representation of input string + */ +var toHexNative = function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + return hex; +}; -module.exports = { - methods: methods, - properties: properties +/** + * Shold be called to get hex representation (prefixed by 0x) of ascii string + * + * @method fromAscii + * @param {String} string + * @param {Number} optional padding + * @returns {String} hex representation of input string + */ +var fromAscii = function(str, pad) { + pad = pad === undefined ? 0 : pad; + var hex = toHexNative(str); + while (hex.length < pad*2) + hex += "00"; + return "0x" + hex; }; +/** + * Should be called to get display name of contract function + * + * @method extractDisplayName + * @param {String} name of function/event + * @returns {String} 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; +}; -},{"./formatters":8}],6:[function(require,module,exports){ -/* - This file is part of ethereum.js. +/// @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)).replace(' ', '') : ""; +}; - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +/** + * Filters all functions from input abi + * + * @method filterFunctions + * @param {Array} abi + * @returns {Array} abi array with filtered objects of type 'function' + */ +var filterFunctions = function (json) { + return json.filter(function (current) { + return current.type === 'function'; + }); +}; - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. +/** + * Filters all events from input abi + * + * @method filterEvents + * @param {Array} abi + * @returns {Array} abi array with filtered objects of type 'event' + */ +var filterEvents = function (json) { + return json.filter(function (current) { + return current.type === 'event'; + }); +}; - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file event.js - * @authors: - * Marek Kotewicz - * @date 2014 +/** + * Converts value to it's decimal representation in string + * + * @method toDecimal + * @param {String|Number|BigNumber} + * @return {String} */ +var toDecimal = function (value) { + return toBigNumber(value).toNumber(); +}; -var abi = require('./abi'); -var utils = require('./utils'); -var signature = require('./signature'); +/** + * Converts value to it's hex representation + * + * @method fromDecimal + * @param {String|Number|BigNumber} + * @return {String} + */ +var fromDecimal = function (value) { + var number = toBigNumber(value); + var result = number.toString(16); -/// filter inputs array && returns only indexed (or not) inputs -/// @param inputs array -/// @param bool if result should be an array of indexed params on not -/// @returns array of (not?) indexed params -var filterInputs = function (inputs, indexed) { - return inputs.filter(function (current) { - return current.indexed === indexed; - }); + return (number.lessThan(0)) + ? '-0x' + result.substr(1) + : '0x' + result; }; -var inputWithName = function (inputs, name) { - var index = utils.findIndex(inputs, function (input) { - return input.name === name; - }); - - if (index === -1) { - console.error('indexed param with name ' + name + ' not found'); - return undefined; +/** + * Auto converts any given value into it's hex representation. + * + * And even stringifys objects before. + * + * @method toHex + * @param {String|Number|BigNumber|Object} + * @return {String} + */ +var toHex = function (val) { + /*jshint maxcomplexity:5 */ + + if(typeof val === 'boolean') + return val; + + if(isBigNumber(val)) + return fromDecimal(val); + + if(typeof val === 'object') + return fromAscii(JSON.stringify(val)); + + if(isString(val) && val.indexOf('0x') === 0) + return val; + // if its a negative number, pass it through fromDecimal + if(isString(val) && val.indexOf('-0x') === 0) + return fromDecimal(val); + + if(isString(val) && !isFinite(val)) + return fromAscii(val); + + if(isFinite(val)) + return fromDecimal(val); + + return val; +}; + +/** + * Returns value of unit in Wei + * + * @method getValueOfUnit + * @param {String} unit the unit to convert to, default ether + * @returns {BigNumber} value of the unit (in Wei) + * @throws error if the unit is not correct:w + */ +var getValueOfUnit = function (unit) { + unit = unit ? unit.toLowerCase() : 'ether'; + var unitValue = unitMap[unit]; + if (unitValue === undefined) { + throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2)); } - return inputs[index]; + return new BigNumber(unitValue, 10); }; -var indexedParamsToTopics = function (event, indexed) { - // sort keys? - return Object.keys(indexed).map(function (key) { - var inputs = [inputWithName(filterInputs(event.inputs, true), key)]; +/** + * Takes a number of wei and converts it to any other ether unit. + * + * Possible units are: + * - kwei/ada + * - mwei/babbage + * - gwei/shannon + * - szabo + * - finney + * - ether + * - kether/grand/einstein + * - mether + * - gether + * - tether + * + * @method fromWei + * @param {Number|String} number can be a number, number string or a HEX of a decimal + * @param {String} unit the unit to convert to, default ether + * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number +*/ +var fromWei = function(number, unit) { + var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); - var value = indexed[key]; - if (value instanceof Array) { - return value.map(function (v) { - return abi.formatInput(inputs, [v]); - }); - } - return abi.formatInput(inputs, [value]); - }); + return (isBigNumber(number)) + ? returnValue : returnValue.toString(10); }; -var inputParser = function (address, sign, event) { - - // valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.filter' - return function (indexed, options) { - var o = options || {}; - o.address = address; - o.topic = []; - o.topic.push(sign); - if (indexed) { - o.topic = o.topic.concat(indexedParamsToTopics(event, indexed)); - } - return o; - }; +/** + * Takes a number of a unit and converts it to wei. + * + * Possible units are: + * - kwei/ada + * - mwei/babbage + * - gwei/shannon + * - szabo + * - finney + * - ether + * - kether/grand/einstein + * - mether + * - gether + * - tether + * + * @method toWei + * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal + * @param {String} unit the unit to convert from, default ether + * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number +*/ +var toWei = function(number, unit) { + var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); + + return (isBigNumber(number)) + ? returnValue : returnValue.toString(10); }; -var getArgumentsObject = function (inputs, indexed, notIndexed) { - var indexedCopy = indexed.slice(); - var notIndexedCopy = notIndexed.slice(); - return inputs.reduce(function (acc, current) { - var value; - if (current.indexed) - value = indexedCopy.splice(0, 1)[0]; - else - value = notIndexedCopy.splice(0, 1)[0]; +/** + * Takes an input and transforms it into an bignumber + * + * @method toBigNumber + * @param {Number|String|BigNumber} a number, string, HEX string or BigNumber + * @return {BigNumber} BigNumber +*/ +var toBigNumber = function(number) { + number = number || 0; + if (isBigNumber(number)) + return number; - acc[current.name] = value; - return acc; - }, {}); + return (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) + ? new BigNumber(number.replace('0x',''), 16) + : new BigNumber(number.toString(10), 10); }; - -var outputParser = function (event) { - - return function (output) { - var result = { - event: utils.extractDisplayName(event.name), - number: output.number, - hash: output.hash, - args: {} - }; - output.topics = output.topic; // fallback for go-ethereum - if (!output.topic) { - return result; - } - - var indexedOutputs = filterInputs(event.inputs, true); - var indexedData = "0x" + output.topic.slice(1, output.topic.length).map(function (topic) { return topic.slice(2); }).join(""); - var indexedRes = abi.formatOutput(indexedOutputs, indexedData); +/** + * Takes and input transforms it into bignumber and if it is negative value, into two's complement + * + * @method toTwosComplement + * @param {Number|String|BigNumber} + * @return {BigNumber} + */ +var toTwosComplement = function (number) { + var bigNumber = toBigNumber(number); + if (bigNumber.lessThan(0)) { + return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1); + } + return bigNumber; +}; - var notIndexedOutputs = filterInputs(event.inputs, false); - var notIndexedRes = abi.formatOutput(notIndexedOutputs, output.data); +/** + * Checks if the given string has proper length + * + * @method isAddress + * @param {String} address the given HEX adress + * @return {Boolean} +*/ +var isAddress = function(address) { + if (!isString(address)) { + return false; + } - result.args = getArgumentsObject(event.inputs, indexedRes, notIndexedRes); + return ((address.indexOf('0x') === 0 && address.length === 42) || + (address.indexOf('0x') === -1 && address.length === 40)); +}; - return result; - }; +/** + * Returns true if object is BigNumber, otherwise false + * + * @method isBigNumber + * @param {Object} + * @return {Boolean} + */ +var isBigNumber = function (object) { + return object instanceof BigNumber || + (object && object.constructor && object.constructor.name === 'BigNumber'); }; -var getMatchingEvent = function (events, payload) { - for (var i = 0; i < events.length; i++) { - var sign = signature.eventSignatureFromAscii(events[i].name); - if (sign === payload.topic[0]) { - return events[i]; - } - } - return undefined; +/** + * Returns true if object is string, otherwise false + * + * @method isString + * @param {Object} + * @return {Boolean} + */ +var isString = function (object) { + return typeof object === 'string' || + (object && object.constructor && object.constructor.name === 'String'); }; +/** + * Returns true if object is function, otherwise false + * + * @method isFunction + * @param {Object} + * @return {Boolean} + */ +var isFunction = function (object) { + return typeof object === 'function'; +}; module.exports = { - inputParser: inputParser, - outputParser: outputParser, - getMatchingEvent: getMatchingEvent + findIndex: findIndex, + toHex: toHex, + toDecimal: toDecimal, + fromDecimal: fromDecimal, + toAscii: toAscii, + fromAscii: fromAscii, + extractDisplayName: extractDisplayName, + extractTypeName: extractTypeName, + filterFunctions: filterFunctions, + filterEvents: filterEvents, + toWei: toWei, + fromWei: fromWei, + toBigNumber: toBigNumber, + toTwosComplement: toTwosComplement, + isBigNumber: isBigNumber, + isAddress: isAddress, + isFunction: isFunction, + isString: isString }; -},{"./abi":1,"./signature":14,"./utils":16}],7:[function(require,module,exports){ +},{}],6:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -806,7 +1051,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file filter.js +/** @file web3.js * @authors: * Jeffrey Wilcke * Marek Kotewicz @@ -815,119 +1060,263 @@ module.exports = { * @date 2014 */ -/// Should be called to check if filter implementation is valid -/// @returns true if it is, otherwise false -var implementationIsValid = function (i) { - return !!i && - typeof i.newFilter === 'function' && - typeof i.getLogs === 'function' && - typeof i.uninstallFilter === 'function' && - typeof i.startPolling === 'function' && - typeof i.stopPolling === 'function'; +var net = require('./web3/net'); +var eth = require('./web3/eth'); +var db = require('./web3/db'); +var shh = require('./web3/shh'); +var watches = require('./web3/watches'); +var filter = require('./web3/filter'); +var utils = require('./utils/utils'); +var formatters = require('./solidity/formatters'); +var requestManager = require('./web3/requestmanager'); +var c = require('./utils/config'); + +/// @returns an array of objects describing web3 api methods +var web3Methods = function () { + return [ + { name: 'sha3', call: 'web3_sha3' } + ]; +}; + +/// creates methods in a given object based on method description on input +/// setups api calls for these methods +var setupMethods = function (obj, methods) { + methods.forEach(function (method) { + // allow for object methods 'myObject.method' + var objectMethods = method.name.split('.'), + callFunction = function () { + /*jshint maxcomplexity:8 */ + + var callback = null, + args = Array.prototype.slice.call(arguments), + call = typeof method.call === 'function' ? method.call(args) : method.call; + + // get the callback if one is available + if(typeof args[args.length-1] === 'function'){ + callback = args[args.length-1]; + Array.prototype.pop.call(args); + } + + // add the defaultBlock if not given + if(method.addDefaultblock) { + if(args.length !== method.addDefaultblock) + Array.prototype.push.call(args, (isFinite(c.ETH_DEFAULTBLOCK) ? utils.fromDecimal(c.ETH_DEFAULTBLOCK) : c.ETH_DEFAULTBLOCK)); + else + args[args.length-1] = isFinite(args[args.length-1]) ? utils.fromDecimal(args[args.length-1]) : args[args.length-1]; + } + + // show deprecated warning + if(method.newMethod) + console.warn('This method is deprecated please use web3.'+ method.newMethod +'() instead.'); + + return web3.manager.send({ + method: call, + params: args, + outputFormatter: method.outputFormatter, + inputFormatter: method.inputFormatter, + addDefaultblock: method.addDefaultblock + }, callback); + }; + + if(objectMethods.length > 1) { + if(!obj[objectMethods[0]]) + obj[objectMethods[0]] = {}; + + obj[objectMethods[0]][objectMethods[1]] = callFunction; + + } else { + + obj[objectMethods[0]] = callFunction; + } + + }); }; -/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones -/// @param should be string or object -/// @returns options string or object -var getOptions = function (options) { - if (typeof options === 'string') { - return options; - } +/// creates properties in a given object based on properties description on input +/// setups api calls for these properties +var setupProperties = function (obj, properties) { + properties.forEach(function (property) { + var proto = {}; + proto.get = function () { + + // show deprecated warning + if(property.newProperty) + console.warn('This property is deprecated please use web3.'+ property.newProperty +' instead.'); + + + return web3.manager.send({ + method: property.getter, + outputFormatter: property.outputFormatter + }); + }; + + if (property.setter) { + proto.set = function (val) { + + // show deprecated warning + if(property.newProperty) + console.warn('This property is deprecated please use web3.'+ property.newProperty +' instead.'); + + return web3.manager.send({ + method: property.setter, + params: [val], + inputFormatter: property.inputFormatter + }); + }; + } + + proto.enumerable = !property.newProperty; + Object.defineProperty(obj, property.name, proto); + + }); +}; + +/*jshint maxparams:4 */ +var startPolling = function (method, id, callback, uninstall) { + web3.manager.startPolling({ + method: method, + params: [id] + }, id, callback, uninstall); +}; +/*jshint maxparams:3 */ + +var stopPolling = function (id) { + web3.manager.stopPolling(id); +}; + +var ethWatch = { + startPolling: startPolling.bind(null, 'eth_getFilterChanges'), + stopPolling: stopPolling +}; + +var shhWatch = { + startPolling: startPolling.bind(null, 'shh_getFilterChanges'), + stopPolling: stopPolling +}; + +/// setups web3 object, and it's in-browser executed methods +var web3 = { + manager: requestManager(), + providers: {}, + + setProvider: function (provider) { + web3.manager.setProvider(provider); + }, + + /// Should be called to reset state of web3 object + /// Resets everything except manager + reset: function () { + web3.manager.reset(); + }, + + /// @returns hex string of the input + toHex: utils.toHex, - options = options || {}; + /// @returns ascii string representation of hex value prefixed with 0x + toAscii: utils.toAscii, - if (options.topics) { - console.warn('"topics" is deprecated, is "topic" instead'); - } + /// @returns hex representation (prefixed by 0x) of ascii string + fromAscii: utils.fromAscii, - // evaluate lazy properties - return { - to: options.to, - topic: options.topic, - earliest: options.earliest, - latest: options.latest, - max: options.max, - skip: options.skip, - address: options.address - }; -}; + /// @returns decimal representaton of hex value prefixed by 0x + toDecimal: utils.toDecimal, -/// Should be used when we want to watch something -/// it's using inner polling mechanism and is notified about changes -/// @param options are filter options -/// @param implementation, an abstract polling implementation -/// @param formatter (optional), callback function which formats output before 'real' callback -var filter = function(options, implementation, formatter) { - if (!implementationIsValid(implementation)) { - console.error('filter implemenation is invalid'); - return; - } + /// @returns hex representation (prefixed by 0x) of decimal value + fromDecimal: utils.fromDecimal, - options = getOptions(options); - var callbacks = []; - var filterId = implementation.newFilter(options); - var onMessages = function (messages) { - messages.forEach(function (message) { - message = formatter ? formatter(message) : message; - callbacks.forEach(function (callback) { - callback(message); - }); - }); - }; + /// @returns a BigNumber object + toBigNumber: utils.toBigNumber, - implementation.startPolling(filterId, onMessages, implementation.uninstallFilter); + toWei: utils.toWei, + fromWei: utils.fromWei, + isAddress: utils.isAddress, - var watch = function(callback) { - callbacks.push(callback); - }; + // provide network information + net: { + // peerCount: + }, - var stopWatching = function() { - implementation.stopPolling(filterId); - implementation.uninstallFilter(filterId); - callbacks = []; - }; - var get = function () { - return implementation.getLogs(filterId); - }; - - return { - watch: watch, - stopWatching: stopWatching, - get: get, + /// eth object prototype + eth: { + // DEPRECATED + contractFromAbi: function (abi) { + console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); - // DEPRECATED methods - changed: function(){ - console.warn('watch().changed() is deprecated please use filter().watch() instead.'); - return watch.apply(this, arguments); - }, - arrived: function(){ - console.warn('watch().arrived() is deprecated please use filter().watch() instead.'); - return watch.apply(this, arguments); - }, - happened: function(){ - console.warn('watch().happened() is deprecated please use filter().watch() instead.'); - return watch.apply(this, arguments); + return function(addr) { + // Default to address of Config. TODO: rremove prior to genesis. + addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; + var ret = web3.eth.contract(addr, abi); + ret.address = addr; + return ret; + }; }, - uninstall: function(){ - console.warn('watch().uninstall() is deprecated please use filter().stopWatching() instead.'); - return stopWatching.apply(this, arguments); + + /// @param filter may be a string, object or event + /// @param eventParams is optional, this is an object with optional event eventParams params + /// @param options is optional, this is an object with optional event options ('max'...) + /*jshint maxparams:4 */ + filter: function (fil, eventParams, options) { + + // if its event, treat it differently + if (fil._isEvent) + return fil(eventParams, options); + + return filter(fil, ethWatch, formatters.outputLogFormatter); }, - messages: function(){ - console.warn('watch().messages() is deprecated please use filter().get() instead.'); - return get.apply(this, arguments); + // DEPRECATED + watch: function (fil, eventParams, options) { + console.warn('eth.watch() is deprecated please use eth.filter() instead.'); + return this.filter(fil, eventParams, options); + } + /*jshint maxparams:3 */ + }, + + /// db object prototype + db: {}, + + /// shh object prototype + shh: { + /// @param filter may be a string, object or event + filter: function (fil) { + return filter(fil, shhWatch, formatters.outputPostFormatter); }, - logs: function(){ - console.warn('watch().logs() is deprecated please use filter().get() instead.'); - return get.apply(this, arguments); + // DEPRECATED + watch: function (fil) { + console.warn('shh.watch() is deprecated please use shh.filter() instead.'); + return this.filter(fil); } - }; + } }; -module.exports = filter; + +// ADD defaultblock +Object.defineProperty(web3.eth, 'defaultBlock', { + get: function () { + return c.ETH_DEFAULTBLOCK; + }, + set: function (val) { + c.ETH_DEFAULTBLOCK = val; + return c.ETH_DEFAULTBLOCK; + } +}); -},{}],8:[function(require,module,exports){ +/// setups all api methods +setupMethods(web3, web3Methods()); +setupMethods(web3.net, net.methods); +setupProperties(web3.net, net.properties); +setupMethods(web3.eth, eth.methods); +setupProperties(web3.eth, eth.properties); +setupMethods(web3.db, db.methods()); +setupMethods(web3.shh, shh.methods()); +setupMethods(ethWatch, watches.eth()); +setupMethods(shhWatch, watches.shh()); + +module.exports = web3; + + +},{"./solidity/formatters":2,"./utils/config":4,"./utils/utils":5,"./web3/db":8,"./web3/eth":9,"./web3/filter":11,"./web3/net":15,"./web3/requestmanager":17,"./web3/shh":18,"./web3/watches":20}],7:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -944,289 +1333,235 @@ module.exports = filter; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file formatters.js +/** @file contract.js * @authors: * Marek Kotewicz - * @date 2015 + * @date 2014 */ -if ("build" !== 'build') {/* - var BigNumber = require('bignumber.js'); // jshint ignore:line -*/} - -var utils = require('./utils'); -var c = require('./const'); - -/// @param string string to be padded -/// @param number of characters that result string should have -/// @param sign, by default 0 -/// @returns right aligned string -var padLeft = function (string, chars, sign) { - return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; -}; - -/// Formats input value to byte representation of int -/// If value is negative, return it's two's complement -/// If the value is floating point, round it down -/// @returns right-aligned byte representation of int -var formatInputInt = function (value) { - /*jshint maxcomplexity:7 */ - var padding = c.ETH_PADDING * 2; - if (utils.isBigNumber(value) || typeof value === 'number') { - if (typeof value === 'number') - value = new BigNumber(value); - BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); - value = value.round(); - - if (value.lessThan(0)) - value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1); - value = value.toString(16); - } - else if (typeof value === 'string') { - if (value.indexOf('0x') === 0) { - value = value.substr(2); - } else { - value = formatInputInt(new BigNumber(value)); - } - } - else - value = (+value).toString(16); - return padLeft(value, padding); -}; - -/// Formats input value to byte representation of string -/// @returns left-algined byte representation of string -var formatInputString = function (value) { - return utils.fromAscii(value, c.ETH_PADDING).substr(2); -}; - -/// Formats input value to byte representation of bool -/// @returns right-aligned byte representation bool -var formatInputBool = function (value) { - return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); -}; - -/// Formats input value to byte representation of real -/// Values are multiplied by 2^m and encoded as integers -/// @returns byte representation of real -var formatInputReal = function (value) { - return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); -}; - - -/// Check if input value is negative -/// @param value is hex format -/// @returns true if it is negative, otherwise false -var signedIsNegative = function (value) { - return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; -}; - -/// Formats input right-aligned input bytes to int -/// @returns right-aligned input bytes formatted to int -var formatOutputInt = function (value) { - - value = value || "0"; - - // check if it's negative number - // it it is, return two's complement - if (signedIsNegative(value)) { - return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); - } - return new BigNumber(value, 16); -}; - - -/// Formats big right-aligned input bytes to uint -/// @returns right-aligned input bytes formatted to uint -var formatOutputUInt = function (value) { - value = value || "0"; - return new BigNumber(value, 16); -}; - -/// @returns input bytes formatted to real -var formatOutputReal = function (value) { - return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/// @returns input bytes formatted to ureal -var formatOutputUReal = function (value) { - return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/// @returns right-aligned input bytes formatted to hex -var formatOutputHash = function (value) { - return "0x" + value; -}; +var web3 = require('../web3'); +var abi = require('../solidity/abi'); +var utils = require('../utils/utils'); +var eventImpl = require('./event'); +var signature = require('./signature'); -/// @returns right-aligned input bytes formatted to bool -var formatOutputBool = function (value) { - return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; +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; }; -/// @returns left-aligned input bytes formatted to ascii string -var formatOutputString = function (value) { - return utils.toAscii(value); -}; +var addFunctionRelatedPropertiesToContract = function (contract) { + + contract.call = function (options) { + contract._isTransaction = false; + contract._options = options; + return contract; + }; -/// @returns right-aligned input bytes formatted to address -var formatOutputAddress = function (value) { - return "0x" + value.slice(value.length - 40, value.length); -}; + contract.sendTransaction = function (options) { + contract._isTransaction = true; + contract._options = options; + return contract; + }; + // DEPRECATED + contract.transact = function (options) { -/// Formats the input to a big number -/// @returns a BigNumber object -var convertToBigNumber = function (value) { + console.warn('myContract.transact() is deprecated please use myContract.sendTransaction() instead.'); - // remove the leading 0x - if(typeof value === 'string') - value = value.replace('0x', ''); + return contract.sendTransaction(options); + }; - value = value || "0"; + contract._options = {}; + ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) { + contract[p] = function (v) { + contract._options[p] = v; + return contract; + }; + }); - return new BigNumber(value, 16); }; +var addFunctionsToContract = function (contract, desc, address) { + var inputParser = abi.inputParser(desc); + var outputParser = abi.outputParser(desc); -/** -Formats the input of a transaction and converts all values to HEX - -@returns object -*/ -var inputTransactionFormatter = function(options){ + // create contract functions + utils.filterFunctions(desc).forEach(function (method) { - // make code -> data - if(options.code) { - options.data = options.code; - delete options.code; - } + var displayName = utils.extractDisplayName(method.name); + var typeName = utils.extractTypeName(method.name); - // make endowment -> value - if(options.endowment) { - options.value = options.endowment; - delete options.endowment; - } + var impl = function () { + /*jshint maxcomplexity:7 */ + var params = Array.prototype.slice.call(arguments); + var sign = signature.functionSignatureFromAscii(method.name); + var parsed = inputParser[displayName][typeName].apply(null, params); + var options = contract._options || {}; + options.to = address; + options.data = sign + parsed; + + var isTransaction = contract._isTransaction === true || (contract._isTransaction !== false && !method.constant); + var collapse = options.collapse !== false; + + // reset + contract._options = {}; + contract._isTransaction = null; - // format the following options - /*jshint maxcomplexity:5 */ - ['gasPrice', 'value'].forEach(function(key){ + if (isTransaction) { + + exportNatspecGlobals({ + abi: desc, + address: address, + method: method.name, + params: params + }); - // if hex or string integer - if(typeof options[key] === 'string') { + // transactions do not have any output, cause we do not know, when they will be processed + web3.eth.sendTransaction(options); + return; + } + + var output = web3.eth.call(options); + var ret = outputParser[displayName][typeName](output); + if (collapse) + { + if (ret.length === 1) + ret = ret[0]; + else if (ret.length === 0) + ret = null; + } + return ret; + }; - // if not hex assume its a number string - if(options[key].indexOf('0x') === -1) - options[key] = utils.fromDecimal(options[key]); + if (contract[displayName] === undefined) { + contract[displayName] = impl; + } - // if number - } else if(typeof options[key] === 'number') { - options[key] = utils.fromDecimal(options[key]); + contract[displayName][typeName] = impl; + }); +}; - // if bignumber - } else if(options[key] instanceof BigNumber) { - options[key] = '0x'+ options[key].toString(16); +var addEventRelatedPropertiesToContract = function (contract, desc, address) { + contract.address = address; + contract._onWatchEventResult = function (data) { + var matchingEvent = event.getMatchingEvent(utils.filterEvents(desc)); + var parser = eventImpl.outputParser(matchingEvent); + return parser(data); + }; + + Object.defineProperty(contract, 'topic', { + get: function() { + return utils.filterEvents(desc).map(function (e) { + return signature.eventSignatureFromAscii(e.name); + }); } }); - // format gas to number - options.gas = Number(options.gas); - - - return options; }; -/** -Formats the output of a transaction to its proper values +var addEventsToContract = function (contract, desc, address) { + // create contract events + utils.filterEvents(desc).forEach(function (e) { -@returns object -*/ -var outputTransactionFormatter = function(tx){ - // transform to number - tx.gas = Number(tx.gas); + var impl = function () { + var params = Array.prototype.slice.call(arguments); + var sign = signature.eventSignatureFromAscii(e.name); + var event = eventImpl.inputParser(address, sign, e); + var o = event.apply(null, params); + var outputFormatter = function (data) { + var parser = eventImpl.outputParser(e); + return parser(data); + }; + return web3.eth.filter(o, undefined, undefined, outputFormatter); + }; + + // this property should be used by eth.filter to check if object is an event + impl._isEvent = true; + + var displayName = utils.extractDisplayName(e.name); + var typeName = utils.extractTypeName(e.name); - // gasPrice to bignumber - if(typeof tx.gasPrice === 'string' && tx.gasPrice.indexOf('0x') === 0) - tx.gasPrice = new BigNumber(tx.gasPrice, 16); - else - tx.gasPrice = new BigNumber(tx.gasPrice.toString(10), 10); + if (contract[displayName] === undefined) { + contract[displayName] = impl; + } - // value to bignumber - if(typeof tx.value === 'string' && tx.value.indexOf('0x') === 0) - tx.value = new BigNumber(tx.value, 16); - else - tx.value = new BigNumber(tx.value.toString(10), 10); + contract[displayName][typeName] = impl; - return tx; + }); }; /** -Formats the output of a block to its proper values + * This method should be called when we want to call / transact some solidity method from javascript + * it returns an object which has same methods available as solidity contract description + * usage example: + * + * var abi = [{ + * name: 'myMethod', + * inputs: [{ name: 'a', type: 'string' }], + * outputs: [{name: 'd', type: 'string' }] + * }]; // contract abi + * + * var MyContract = web3.eth.contract(abi); // creation of contract prototype + * + * var contractInstance = new MyContract('0x0123123121'); + * + * contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default) + * contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit) + * contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction + * + * @param abi - abi json description of the contract, which is being created + * @returns contract object + */ +var contract = function (abi) { -@returns object -*/ -var outputBlockFormatter = function(block){ - /*jshint maxcomplexity:7 */ + // return prototype + if(abi instanceof Array && arguments.length === 1) { + return Contract.bind(null, abi); - // transform to number - block.gasLimit = Number(block.gasLimit); - block.gasUsed = Number(block.gasUsed); - block.size = Number(block.size); - block.timestamp = Number(block.timestamp); - block.number = Number(block.number); - - // minGasPrice to bignumber - if(block.minGasPrice) { - if(typeof block.minGasPrice === 'string' && block.minGasPrice.indexOf('0x') === 0) - block.minGasPrice = new BigNumber(block.minGasPrice, 16); - else - block.minGasPrice = new BigNumber(block.minGasPrice.toString(10), 10); - } + // deprecated: auto initiate contract + } else { + console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); - // difficulty to bignumber - if(block.difficulty) { - if(typeof block.difficulty === 'string' && block.difficulty.indexOf('0x') === 0) - block.difficulty = new BigNumber(block.difficulty, 16); - else - block.difficulty = new BigNumber(block.difficulty.toString(10), 10); + return new Contract(arguments[1], arguments[0]); } +}; - // difficulty to bignumber - if(block.totalDifficulty) { - if(typeof block.totalDifficulty === 'string' && block.totalDifficulty.indexOf('0x') === 0) - block.totalDifficulty = new BigNumber(block.totalDifficulty, 16); - else - block.totalDifficulty = new BigNumber(block.totalDifficulty.toString(10), 10); - } +function Contract(abi, address) { - return block; -}; + // workaround for invalid assumption that method.name is the full anonymous prototype of the method. + // it's not. it's just the name. the rest of the code assumes it's actually the anonymous + // prototype, so we make it so as a workaround. + // TODO: we may not want to modify input params, maybe use copy instead? + abi.forEach(function (method) { + if (method.name.indexOf('(') === -1) { + var displayName = method.name; + var typeName = method.inputs.map(function(i){return i.type; }).join(); + method.name = displayName + '(' + typeName + ')'; + } + }); + var result = {}; + addFunctionRelatedPropertiesToContract(result); + addFunctionsToContract(result, abi, address); + addEventRelatedPropertiesToContract(result, abi, address); + addEventsToContract(result, abi, address); -module.exports = { - formatInputInt: formatInputInt, - formatInputString: formatInputString, - formatInputBool: formatInputBool, - formatInputReal: formatInputReal, - formatOutputInt: formatOutputInt, - formatOutputUInt: formatOutputUInt, - formatOutputReal: formatOutputReal, - formatOutputUReal: formatOutputUReal, - formatOutputHash: formatOutputHash, - formatOutputBool: formatOutputBool, - formatOutputString: formatOutputString, - formatOutputAddress: formatOutputAddress, - convertToBigNumber: convertToBigNumber, - inputTransactionFormatter: inputTransactionFormatter, - outputTransactionFormatter: outputTransactionFormatter, - outputBlockFormatter: outputBlockFormatter -}; + return result; +} + +module.exports = contract; -},{"./const":2,"./utils":16}],9:[function(require,module,exports){ +},{"../solidity/abi":1,"../utils/utils":5,"../web3":6,"./event":10,"./signature":19}],8:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1243,40 +1578,27 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file httpsync.js +/** @file db.js * @authors: * Marek Kotewicz - * Marian Oancea - * @date 2014 + * @date 2015 */ -if ("build" !== 'build') {/* - var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line -*/} - -var HttpSyncProvider = function (host) { - this.handlers = []; - this.host = host || 'http://localhost:8080'; +/// @returns an array of objects describing web3.db api methods +var methods = function () { + return [ + { name: 'put', call: 'db_put' }, + { name: 'get', call: 'db_get' }, + { name: 'putString', call: 'db_putString' }, + { name: 'getString', call: 'db_getString' } + ]; }; -HttpSyncProvider.prototype.send = function (payload) { - //var data = formatJsonRpcObject(payload); - - var request = new XMLHttpRequest(); - request.open('POST', this.host, false); - request.send(JSON.stringify(payload)); - - var result = request.responseText; - // check request.status - if(request.status !== 200) - return; - return JSON.parse(result); +module.exports = { + methods: methods }; -module.exports = HttpSyncProvider; - - -},{}],10:[function(require,module,exports){ +},{}],9:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1293,57 +1615,140 @@ module.exports = HttpSyncProvider; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file jsonrpc.js +/** @file eth.js * @authors: * Marek Kotewicz * @date 2015 */ -var messageId = 1; +/** + * Web3 + * + * @module web3 + */ -/// Should be called to valid json create payload object -/// @param method of jsonrpc call, required -/// @param params, an array of method params, optional -/// @returns valid jsonrpc payload object -var toPayload = function (method, params) { - if (!method) - console.error('jsonrpc method should be specified!'); +/** + * Eth methods and properties + * + * An example method object can look as follows: + * + * { + * name: 'getBlock', + * call: blockCall, + * outputFormatter: formatters.outputBlockFormatter, + * inputFormatter: [ // can be a formatter funciton or an array of functions. Where each item in the array will be used for one parameter + * utils.toHex, // formats paramter 1 + * function(param){ if(!param) return false; } // formats paramter 2 + * ] + * }, + * + * @class [web3] eth + * @constructor + */ - return { - jsonrpc: '2.0', - method: method, - params: params || [], - id: messageId++ - }; + +var formatters = require('./formatters'); +var utils = require('../utils/utils'); + + +var blockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; +}; + +var transactionFromBlockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; +}; + +var uncleCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex'; }; -/// Should be called to check if jsonrpc response is valid -/// @returns true if response is valid, otherwise false -var isValidResponse = function (response) { - return !!response && - !response.error && - response.jsonrpc === '2.0' && - typeof response.id === 'number' && - response.result !== undefined; // only undefined is not valid json object +var getBlockTransactionCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber'; }; -/// Should be called to create batch payload object -/// @param messages, an array of objects with method (required) and params (optional) fields -var toBatchPayload = function (messages) { - return messages.map(function (message) { - return toPayload(message.method, message.params); - }); +var uncleCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber'; }; +/// @returns an array of objects describing web3.eth api methods +var methods = [ + { name: 'getBalance', call: 'eth_getBalance', addDefaultblock: 2, + outputFormatter: formatters.convertToBigNumber}, + { name: 'getStorage', call: 'eth_getStorage', addDefaultblock: 2}, + { name: 'getStorageAt', call: 'eth_getStorageAt', addDefaultblock: 3, + inputFormatter: utils.toHex}, + { name: 'getData', call: 'eth_getData', addDefaultblock: 2}, + { name: 'getBlock', call: blockCall, + outputFormatter: formatters.outputBlockFormatter, + inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]}, + { name: 'getUncle', call: uncleCall, + outputFormatter: formatters.outputBlockFormatter, + inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]}, + { name: 'getCompilers', call: 'eth_getCompilers' }, + { name: 'getBlockTransactionCount', call: getBlockTransactionCountCall, + outputFormatter: utils.toDecimal, + inputFormatter: utils.toHex }, + { name: 'getBlockUncleCount', call: uncleCountCall, + outputFormatter: utils.toDecimal, + inputFormatter: utils.toHex }, + { name: 'getTransaction', call: 'eth_getTransactionByHash', + outputFormatter: formatters.outputTransactionFormatter }, + { name: 'getTransactionFromBlock', call: transactionFromBlockCall, + outputFormatter: formatters.outputTransactionFormatter, + inputFormatter: utils.toHex }, + { name: 'getTransactionCount', call: 'eth_getTransactionCount', addDefaultblock: 2, + outputFormatter: utils.toDecimal}, + { name: 'sendTransaction', call: 'eth_sendTransaction', + inputFormatter: formatters.inputTransactionFormatter }, + { name: 'call', call: 'eth_call', addDefaultblock: 2, + inputFormatter: formatters.inputCallFormatter }, + { name: 'compile.solidity', call: 'eth_compileSolidity' }, + { name: 'compile.lll', call: 'eth_compileLLL' }, + { name: 'compile.serpent', call: 'eth_compileSerpent' }, + { name: 'flush', call: 'eth_flush' }, + + // deprecated methods + { name: 'balanceAt', call: 'eth_balanceAt', newMethod: 'eth.getBalance' }, + { name: 'stateAt', call: 'eth_stateAt', newMethod: 'eth.getStorageAt' }, + { name: 'storageAt', call: 'eth_storageAt', newMethod: 'eth.getStorage' }, + { name: 'countAt', call: 'eth_countAt', newMethod: 'eth.getTransactionCount' }, + { name: 'codeAt', call: 'eth_codeAt', newMethod: 'eth.getData' }, + { name: 'transact', call: 'eth_transact', newMethod: 'eth.sendTransaction' }, + { name: 'block', call: blockCall, newMethod: 'eth.getBlock' }, + { name: 'transaction', call: transactionFromBlockCall, newMethod: 'eth.getTransaction' }, + { name: 'uncle', call: uncleCall, newMethod: 'eth.getUncle' }, + { name: 'compilers', call: 'eth_compilers', newMethod: 'eth.getCompilers' }, + { name: 'solidity', call: 'eth_solidity', newMethod: 'eth.compile.solidity' }, + { name: 'lll', call: 'eth_lll', newMethod: 'eth.compile.lll' }, + { name: 'serpent', call: 'eth_serpent', newMethod: 'eth.compile.serpent' }, + { name: 'transactionCount', call: getBlockTransactionCountCall, newMethod: 'eth.getBlockTransactionCount' }, + { name: 'uncleCount', call: uncleCountCall, newMethod: 'eth.getBlockUncleCount' }, + { name: 'logs', call: 'eth_logs' } +]; + +/// @returns an array of objects describing web3.eth api properties +var properties = [ + { name: 'coinbase', getter: 'eth_coinbase'}, + { name: 'mining', getter: 'eth_mining'}, + { name: 'gasPrice', getter: 'eth_gasPrice', outputFormatter: formatters.convertToBigNumber}, + { name: 'accounts', getter: 'eth_accounts' }, + { name: 'blockNumber', getter: 'eth_blockNumber', outputFormatter: utils.toDecimal}, + + // deprecated properties + { name: 'listening', getter: 'net_listening', setter: 'eth_setListening', newProperty: 'net.listening'}, + { name: 'peerCount', getter: 'net_peerCount', newProperty: 'net.peerCount'}, + { name: 'number', getter: 'eth_number', newProperty: 'eth.blockNumber'} +]; + + module.exports = { - toPayload: toPayload, - isValidResponse: isValidResponse, - toBatchPayload: toBatchPayload + methods: methods, + properties: properties }; - -},{}],11:[function(require,module,exports){ +},{"../utils/utils":5,"./formatters":12}],10:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1360,25 +1765,130 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file qtsync.js +/** @file event.js * @authors: * Marek Kotewicz - * Marian Oancea * @date 2014 */ -var QtSyncProvider = function () { +var abi = require('../solidity/abi'); +var utils = require('../utils/utils'); +var signature = require('./signature'); + +/// filter inputs array && returns only indexed (or not) inputs +/// @param inputs array +/// @param bool if result should be an array of indexed params on not +/// @returns array of (not?) indexed params +var filterInputs = function (inputs, indexed) { + return inputs.filter(function (current) { + return current.indexed === indexed; + }); }; -QtSyncProvider.prototype.send = function (payload) { - var result = navigator.qt.callMethod(JSON.stringify(payload)); - return JSON.parse(result); +var inputWithName = function (inputs, name) { + var index = utils.findIndex(inputs, function (input) { + return input.name === name; + }); + + if (index === -1) { + console.error('indexed param with name ' + name + ' not found'); + return undefined; + } + return inputs[index]; }; -module.exports = QtSyncProvider; +var indexedParamsToTopics = function (event, indexed) { + // sort keys? + return Object.keys(indexed).map(function (key) { + var inputs = [inputWithName(filterInputs(event.inputs, true), key)]; + + var value = indexed[key]; + if (value instanceof Array) { + return value.map(function (v) { + return abi.formatInput(inputs, [v]); + }); + } + return abi.formatInput(inputs, [value]); + }); +}; + +var inputParser = function (address, sign, event) { + + // valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.filter' + return function (indexed, options) { + var o = options || {}; + o.address = address; + o.topic = []; + o.topic.push(sign); + if (indexed) { + o.topic = o.topic.concat(indexedParamsToTopics(event, indexed)); + } + return o; + }; +}; + +var getArgumentsObject = function (inputs, indexed, notIndexed) { + var indexedCopy = indexed.slice(); + var notIndexedCopy = notIndexed.slice(); + return inputs.reduce(function (acc, current) { + var value; + if (current.indexed) + value = indexedCopy.splice(0, 1)[0]; + else + value = notIndexedCopy.splice(0, 1)[0]; + + acc[current.name] = value; + return acc; + }, {}); +}; + +var outputParser = function (event) { + + return function (output) { + var result = { + event: utils.extractDisplayName(event.name), + number: output.number, + hash: output.hash, + args: {} + }; + + output.topics = output.topic; // fallback for go-ethereum + if (!output.topic) { + return result; + } + + var indexedOutputs = filterInputs(event.inputs, true); + var indexedData = "0x" + output.topic.slice(1, output.topic.length).map(function (topic) { return topic.slice(2); }).join(""); + var indexedRes = abi.formatOutput(indexedOutputs, indexedData); + + var notIndexedOutputs = filterInputs(event.inputs, false); + var notIndexedRes = abi.formatOutput(notIndexedOutputs, output.data); + + result.args = getArgumentsObject(event.inputs, indexedRes, notIndexedRes); + + return result; + }; +}; + +var getMatchingEvent = function (events, payload) { + for (var i = 0; i < events.length; i++) { + var sign = signature.eventSignatureFromAscii(events[i].name); + if (sign === payload.topic[0]) { + return events[i]; + } + } + return undefined; +}; + + +module.exports = { + inputParser: inputParser, + outputParser: outputParser, + getMatchingEvent: getMatchingEvent +}; -},{}],12:[function(require,module,exports){ +},{"../solidity/abi":1,"../utils/utils":5,"./signature":19}],11:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1395,7 +1905,7 @@ module.exports = QtSyncProvider; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file requestmanager.js +/** @file filter.js * @authors: * Jeffrey Wilcke * Marek Kotewicz @@ -1404,107 +1914,132 @@ module.exports = QtSyncProvider; * @date 2014 */ -var jsonrpc = require('./jsonrpc'); -var c = require('./const'); +var utils = require('../utils/utils'); -/** - * It's responsible for passing messages to providers - * It's also responsible for polling the ethereum node for incoming messages - * Default poll timeout is 1 second - */ -var requestManager = function() { - var polls = []; - var timeout = null; - var provider; +/// Should be called to check if filter implementation is valid +/// @returns true if it is, otherwise false +var implementationIsValid = function (i) { + return !!i && + typeof i.newFilter === 'function' && + typeof i.getLogs === 'function' && + typeof i.uninstallFilter === 'function' && + typeof i.startPolling === 'function' && + typeof i.stopPolling === 'function'; +}; - var send = function (data) { - /*jshint maxcomplexity: 6 */ +/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones +/// @param should be string or object +/// @returns options string or object +var getOptions = function (options) { + /*jshint maxcomplexity:5 */ - // format the input before sending - if(typeof data.inputFormatter === 'function') { - data.params = Array.prototype.map.call(data.params, function(item){ - return data.inputFormatter(item); - }); - } + if (typeof options === 'string') { + return options; + } - var payload = jsonrpc.toPayload(data.method, data.params); - - if (!provider) { - console.error('provider is not set'); - return null; - } + options = options || {}; - var result = provider.send(payload); + if (options.topics) + console.warn('"topics" is deprecated, is "topic" instead'); - if (!jsonrpc.isValidResponse(result)) { - console.log(result); - if(typeof result === 'object' && result.error && result.error.message) - console.error(result.error.message); - return null; - } - - // format the output - return (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result; - }; + // make sure topics, get converted to hex + if(options.topic instanceof Array) { + options.topic = options.topic.map(function(topic){ + return utils.toHex(topic); + }); + } - var setProvider = function (p) { - provider = p; - }; - /*jshint maxparams:4 */ - var startPolling = function (data, pollId, callback, uninstall) { - polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall}); + // evaluate lazy properties + return { + to: options.to, + topic: options.topic, + earliest: options.earliest, + latest: options.latest, + max: options.max, + skip: options.skip, + address: options.address }; - /*jshint maxparams:3 */ +}; - var stopPolling = function (pollId) { - for (var i = polls.length; i--;) { - var poll = polls[i]; - if (poll.id === pollId) { - polls.splice(i, 1); - } - } - }; +/// Should be used when we want to watch something +/// it's using inner polling mechanism and is notified about changes +/// @param options are filter options +/// @param implementation, an abstract polling implementation +/// @param formatter (optional), callback function which formats output before 'real' callback +var filter = function(options, implementation, formatter) { + if (!implementationIsValid(implementation)) { + console.error('filter implemenation is invalid'); + return; + } - var reset = function () { - polls.forEach(function (poll) { - poll.uninstall(poll.id); + options = getOptions(options); + var callbacks = []; + var filterId = implementation.newFilter(options); + + // call the callbacks + var onMessages = function (messages) { + messages.forEach(function (message) { + message = formatter ? formatter(message) : message; + callbacks.forEach(function (callback) { + callback(message); + }); }); - polls = []; + }; + + implementation.startPolling(filterId, onMessages, implementation.uninstallFilter); + + var watch = function(callback) { + callbacks.push(callback); + }; - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - poll(); + var stopWatching = function() { + implementation.stopPolling(filterId); + implementation.uninstallFilter(filterId); + callbacks = []; }; - var poll = function () { - polls.forEach(function (data) { - var result = send(data.data); - if (!(result instanceof Array) || result.length === 0) { - return; - } - data.callback(result); - }); - timeout = setTimeout(poll, c.ETH_POLLING_TIMEOUT); + var get = function () { + return implementation.getLogs(filterId); }; - poll(); - return { - send: send, - setProvider: setProvider, - startPolling: startPolling, - stopPolling: stopPolling, - reset: reset + watch: watch, + stopWatching: stopWatching, + get: get, + + // DEPRECATED methods + changed: function(){ + console.warn('watch().changed() is deprecated please use filter().watch() instead.'); + return watch.apply(this, arguments); + }, + arrived: function(){ + console.warn('watch().arrived() is deprecated please use filter().watch() instead.'); + return watch.apply(this, arguments); + }, + happened: function(){ + console.warn('watch().happened() is deprecated please use filter().watch() instead.'); + return watch.apply(this, arguments); + }, + uninstall: function(){ + console.warn('watch().uninstall() is deprecated please use filter().stopWatching() instead.'); + return stopWatching.apply(this, arguments); + }, + messages: function(){ + console.warn('watch().messages() is deprecated please use filter().get() instead.'); + return get.apply(this, arguments); + }, + logs: function(){ + console.warn('watch().logs() is deprecated please use filter().get() instead.'); + return get.apply(this, arguments); + } }; }; -module.exports = requestManager; +module.exports = filter; -},{"./const":2,"./jsonrpc":10}],13:[function(require,module,exports){ +},{"../utils/utils":5}],12:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1521,32 +2056,191 @@ module.exports = requestManager; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file shh.js +/** @file formatters.js * @authors: * Marek Kotewicz + * Fabian Vogelsteller * @date 2015 */ -/// @returns an array of objects describing web3.shh api methods -var methods = function () { - return [ - { name: 'post', call: 'shh_post' }, - { name: 'newIdentity', call: 'shh_newIdentity' }, - { name: 'hasIdentity', call: 'shh_haveIdentity' }, - { name: 'newGroup', call: 'shh_newGroup' }, - { name: 'addToGroup', call: 'shh_addToGroup' }, +var utils = require('../utils/utils'); - // deprecated - { name: 'haveIdentity', call: 'shh_haveIdentity', newMethod: 'hasIdentity' }, - ]; +/** + * Should the input to a big number + * + * @method convertToBigNumber + * @param {String|Number|BigNumber} + * @returns {BigNumber} object + */ +var convertToBigNumber = function (value) { + return utils.toBigNumber(value); +}; + +/** + * Formats the input of a transaction and converts all values to HEX + * + * @method inputTransactionFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputTransactionFormatter = function (options){ + + // make code -> data + if (options.code) { + options.data = options.code; + delete options.code; + } + + ['gasPrice', 'gas', 'value'].forEach(function(key){ + options[key] = utils.fromDecimal(options[key]); + }); + + return options; +}; + +/** + * Formats the output of a transaction to its proper values + * + * @method outputTransactionFormatter + * @param {Object} transaction + * @returns {Object} transaction +*/ +var outputTransactionFormatter = function (tx){ + tx.gas = utils.toDecimal(tx.gas); + tx.gasPrice = utils.toBigNumber(tx.gasPrice); + tx.value = utils.toBigNumber(tx.value); + return tx; +}; + +/** + * Formats the input of a call and converts all values to HEX + * + * @method inputCallFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputCallFormatter = function (options){ + + // make code -> data + if (options.code) { + options.data = options.code; + delete options.code; + } + + return options; +}; + + +/** + * Formats the output of a block to its proper values + * + * @method outputBlockFormatter + * @param {Object} block object + * @returns {Object} block object +*/ +var outputBlockFormatter = function(block){ + + // transform to number + block.gasLimit = utils.toDecimal(block.gasLimit); + block.gasUsed = utils.toDecimal(block.gasUsed); + block.size = utils.toDecimal(block.size); + block.timestamp = utils.toDecimal(block.timestamp); + block.number = utils.toDecimal(block.number); + + block.minGasPrice = utils.toBigNumber(block.minGasPrice); + block.difficulty = utils.toBigNumber(block.difficulty); + block.totalDifficulty = utils.toBigNumber(block.totalDifficulty); + + if(block.transactions instanceof Array) { + block.transactions.forEach(function(item){ + if(!utils.isString(item)) + return outputTransactionFormatter(item); + }); + } + + return block; +}; + +/** + * Formats the output of a log + * + * @method outputLogFormatter + * @param {Object} log object + * @returns {Object} log +*/ +var outputLogFormatter = function(log){ + log.number = utils.toDecimal(log.number); + return log; +}; + + +/** + * Formats the input of a whisper post and converts all values to HEX + * + * @method inputPostFormatter + * @param {Object} transaction object + * @returns {Object} +*/ +var inputPostFormatter = function(post){ + + post.payload = utils.toHex(post.payload); + post.ttl = utils.fromDecimal(post.ttl); + post.workToProve = utils.fromDecimal(post.workToProve); + + if(!(post.topic instanceof Array)) + post.topic = [post.topic]; + + + // format the following options + post.topic = post.topic.map(function(topic){ + return utils.fromAscii(topic); + }); + + return post; +}; + +/** + * Formats the output of a received post message + * + * @method outputPostFormatter + * @param {Object} + * @returns {Object} + */ +var outputPostFormatter = function(post){ + + post.expiry = utils.toDecimal(post.expiry); + post.sent = utils.toDecimal(post.sent); + post.ttl = utils.toDecimal(post.ttl); + post.payloadRaw = post.payload; + post.payload = utils.toAscii(post.payload); + + if(post.payload.indexOf('{') === 0 || post.payload.indexOf('[') === 0) { + try { + post.payload = JSON.parse(post.payload); + } catch (e) { } + } + + // format the following options + post.topic = post.topic.map(function(topic){ + return utils.toAscii(topic); + }); + + return post; }; module.exports = { - methods: methods + convertToBigNumber: convertToBigNumber, + inputTransactionFormatter: inputTransactionFormatter, + outputTransactionFormatter: outputTransactionFormatter, + inputCallFormatter: inputCallFormatter, + outputBlockFormatter: outputBlockFormatter, + outputLogFormatter: outputLogFormatter, + inputPostFormatter: inputPostFormatter, + outputPostFormatter: outputPostFormatter }; -},{}],14:[function(require,module,exports){ +},{"../utils/utils":5}],13:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1563,34 +2257,54 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file signature.js +/** @file httpprovider.js * @authors: * Marek Kotewicz - * @date 2015 + * Marian Oancea + * @date 2014 */ -var web3 = require('./web3'); -var c = require('./const'); +if ("build" !== 'build') {/* + var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line +*/} -/// @param function name for which we want to get signature -/// @returns signature of function with given name -var functionSignatureFromAscii = function (name) { - return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2); +var HttpProvider = function (host) { + this.handlers = []; + this.host = host || 'http://localhost:8080'; }; -/// @param event name for which we want to get signature -/// @returns signature of event with given name -var eventSignatureFromAscii = function (name) { - return web3.sha3(web3.fromAscii(name)); -}; +HttpProvider.prototype.send = function (payload, callback) { + var request = new XMLHttpRequest(); + request.open('POST', this.host, false); -module.exports = { - functionSignatureFromAscii: functionSignatureFromAscii, - eventSignatureFromAscii: eventSignatureFromAscii + // ASYNC + if(typeof callback === 'function') { + request.onreadystatechange = function() { + if(request.readyState === 4 && request.status === 200) { + callback(JSON.parse(request.responseText)); + } + }; + + request.open('POST', this.host, true); + request.send(JSON.stringify(payload)); + + // SYNC + } else { + request.open('POST', this.host, false); + request.send(JSON.stringify(payload)); + + // check request.status + if(request.status !== 200) + return; + return JSON.parse(request.responseText); + + } }; +module.exports = HttpProvider; -},{"./const":2,"./web3":18}],15:[function(require,module,exports){ + +},{}],14:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1607,71 +2321,57 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file types.js +/** @file jsonrpc.js * @authors: * Marek Kotewicz * @date 2015 */ -var f = require('./formatters'); +var messageId = 1; -/// @param expected type prefix (string) -/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false -var prefixedType = function (prefix) { - return function (type) { - return type.indexOf(prefix) === 0; - }; -}; +/// Should be called to valid json create payload object +/// @param method of jsonrpc call, required +/// @param params, an array of method params, optional +/// @returns valid jsonrpc payload object +var toPayload = function (method, params) { + if (!method) + console.error('jsonrpc method should be specified!'); -/// @param expected type name (string) -/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false -var namedType = function (name) { - return function (type) { - return name === type; - }; + return { + jsonrpc: '2.0', + method: method, + params: params || [], + id: messageId++ + }; }; -/// Setups input formatters for solidity types -/// @returns an array of input formatters -var inputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatInputInt }, - { type: prefixedType('int'), format: f.formatInputInt }, - { type: prefixedType('hash'), format: f.formatInputInt }, - { type: prefixedType('string'), format: f.formatInputString }, - { type: prefixedType('real'), format: f.formatInputReal }, - { type: prefixedType('ureal'), format: f.formatInputReal }, - { type: namedType('address'), format: f.formatInputInt }, - { type: namedType('bool'), format: f.formatInputBool } - ]; +/// Should be called to check if jsonrpc response is valid +/// @returns true if response is valid, otherwise false +var isValidResponse = function (response) { + return !!response && + !response.error && + response.jsonrpc === '2.0' && + typeof response.id === 'number' && + response.result !== undefined; // only undefined is not valid json object }; -/// Setups output formaters for solidity types -/// @returns an array of output formatters -var outputTypes = function () { +/// Should be called to create batch payload object +/// @param messages, an array of objects with method (required) and params (optional) fields +var toBatchPayload = function (messages) { + return messages.map(function (message) { + return toPayload(message.method, message.params); + }); +}; - return [ - { type: prefixedType('uint'), format: f.formatOutputUInt }, - { type: prefixedType('int'), format: f.formatOutputInt }, - { type: prefixedType('hash'), format: f.formatOutputHash }, - { type: prefixedType('string'), format: f.formatOutputString }, - { type: prefixedType('real'), format: f.formatOutputReal }, - { type: prefixedType('ureal'), format: f.formatOutputUReal }, - { type: namedType('address'), format: f.formatOutputAddress }, - { type: namedType('bool'), format: f.formatOutputBool } - ]; +module.exports = { + toPayload: toPayload, + isValidResponse: isValidResponse, + toBatchPayload: toBatchPayload }; -module.exports = { - prefixedType: prefixedType, - namedType: namedType, - inputTypes: inputTypes, - outputTypes: outputTypes -}; -},{"./formatters":8}],16:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1688,304 +2388,228 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file utils.js +/** @file eth.js * @authors: * Marek Kotewicz * @date 2015 */ -var c = require('./const'); +var utils = require('../utils/utils'); -if ("build" !== 'build') {/* - var BigNumber = require('bignumber.js'); // jshint ignore:line -*/} +/// @returns an array of objects describing web3.eth api methods +var methods = [ + // { name: 'getBalance', call: 'eth_balanceAt', outputFormatter: formatters.convertToBigNumber}, +]; -var unitMap = { - 'wei': '1', - 'kwei': '1000', - 'ada': '1000', - 'mwei': '1000000', - 'babbage': '1000000', - 'gwei': '1000000000', - 'shannon': '1000000000', - 'szabo': '1000000000000', - 'finney': '1000000000000000', - 'ether': '1000000000000000000', - 'kether': '1000000000000000000000', - 'grand': '1000000000000000000000', - 'einstein': '1000000000000000000000', - 'mether': '1000000000000000000000000', - 'gether': '1000000000000000000000000000', - 'tether': '1000000000000000000000000000000' -}; +/// @returns an array of objects describing web3.eth api properties +var properties = [ + { name: 'listening', getter: 'net_listening'}, + { name: 'peerCount', getter: 'net_peerCount', outputFormatter: utils.toDecimal }, +]; -/// Finds first index of array element matching pattern -/// @param array -/// @param callback pattern -/// @returns index of element -var findIndex = function (array, callback) { - var end = false; - var i = 0; - for (; i < array.length && !end; i++) { - end = callback(array[i]); - } - return end ? i - 1 : -1; +module.exports = { + methods: methods, + properties: properties }; -/// @returns ascii string representation of hex value prefixed with 0x -var toAscii = function(hex) { -// Find termination - var str = ""; - var i = 0, l = hex.length; - if (hex.substring(0, 2) === '0x') { - i = 2; - } - for (; i < l; i+=2) { - var code = parseInt(hex.substr(i, 2), 16); - if (code === 0) { - break; - } - - str += String.fromCharCode(code); - } - - return str; -}; - -var toHex = function(str) { - var hex = ""; - for(var i = 0; i < str.length; i++) { - var n = str.charCodeAt(i).toString(16); - hex += n.length < 2 ? '0' + n : n; - } - return hex; -}; +},{"../utils/utils":5}],16:[function(require,module,exports){ +/* + This file is part of ethereum.js. -/// @returns hex representation (prefixed by 0x) of ascii string -var fromAscii = function(str, pad) { - pad = pad === undefined ? 0 : pad; - var hex = toHex(str); - while (hex.length < pad*2) - hex += "00"; - return "0x" + hex; -}; + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. -/// @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; -}; + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. -/// @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)).replace(' ', '') : ""; -}; + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file qtsync.js + * @authors: + * Marek Kotewicz + * Marian Oancea + * @date 2014 + */ -/// 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'; - }); +var QtSyncProvider = 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'; - }); +QtSyncProvider.prototype.send = function (payload) { + var result = navigator.qt.callMethod(JSON.stringify(payload)); + return JSON.parse(result); }; -/// used to transform value/string to eth string -/// TODO: use BigNumber.js to parse int -/// TODO: add tests for it! -var toEth = function (str) { - - console.warn('This method is deprecated please use eth.fromWei(BigNumberOrNumber, unit) instead.'); - - /*jshint maxcomplexity:7 */ - var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str.replace(/,/g,'').replace(/ /g,'')) : str; - var unit = 0; - var units = c.ETH_UNITS; - while (val > 3000 && unit < units.length - 1) - { - val /= 1000; - unit++; - } - var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); - var replaceFunction = function($0, $1, $2) { - return $1 + ',' + $2; - }; - - while (true) { - var o = s; - s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); - if (o === s) - break; - } - return s + ' ' + units[unit]; -}; +module.exports = QtSyncProvider; -var toDecimal = function (val) { - // remove 0x and place 0, if it's required - val = val.length > 2 ? val.substring(2) : "0"; - return (new BigNumber(val, 16).toString(10)); -}; +},{}],17:[function(require,module,exports){ +/* + This file is part of ethereum.js. -var fromDecimal = function (val) { - return "0x" + (new BigNumber(val).toString(16)); -}; + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. -/** -Takes a number of wei and converts it to any other ether unit. - -Possible units are: - - - kwei/ada - - mwei/babbage - - gwei/shannon - - szabo - - finney - - ether - - kether/grand/einstein - - mether - - gether - - tether - -@method fromWei -@param {Number|String} number can be a number, number string or a HEX of a decimal -@param {String} unit the unit to convert to -@return {String|Object} When given a BigNumber object it returns one as well, otherwise a number + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . */ -var fromWei = function(number, unit) { - /*jshint maxcomplexity: 6 */ - unit = unit.toLowerCase(); - - var isBigNumber = true; - - if(!unitMap[unit]) { - console.warn('This unit doesn\'t exists, please use the one of the following units' , unitMap); - return number; - } +/** @file requestmanager.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Gav Wood + * @date 2014 + */ - if(!number) - return number; +var jsonrpc = require('./jsonrpc'); +var c = require('../utils/config'); - if(typeof number === 'string' && number.indexOf('0x') === 0) { - isBigNumber = false; - number = new BigNumber(number, 16); - } - - if(!(number instanceof BigNumber)) { - isBigNumber = false; - number = new BigNumber(number.toString(10), 10); // toString to prevent errors, the user have to handle giving correct bignums themselves - } +/** + * It's responsible for passing messages to providers + * It's also responsible for polling the ethereum node for incoming messages + * Default poll timeout is 1 second + */ +var requestManager = function() { + var polls = []; + var timeout = null; + var provider; - number = number.dividedBy(new BigNumber(unitMap[unit], 10)); + var send = function (data, callback) { + /*jshint maxcomplexity: 7 */ - return (isBigNumber) ? number : number.toString(10); -}; + // FORMAT BASED ON ONE FORMATTER function + if(typeof data.inputFormatter === 'function') { + data.params = Array.prototype.map.call(data.params, function(item, index){ + // format everything besides the defaultblock, which is already formated + return (!data.addDefaultblock || index+1 < data.addDefaultblock) + ? data.inputFormatter(item) + : item; + }); -/** -Takes a number of a unit and converts it to wei. - -Possible units are: - - - kwei/ada - - mwei/babbage - - gwei/shannon - - szabo - - finney - - ether - - kether/grand/einstein - - mether - - gether - - tether - -@method toWei -@param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal -@param {String} unit the unit to convert to -@return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var toWei = function(number, unit) { - /*jshint maxcomplexity: 6 */ - unit = unit.toLowerCase(); + // FORMAT BASED ON the input FORMATTER ARRAY + } else if(data.inputFormatter instanceof Array) { + data.params = Array.prototype.map.call(data.inputFormatter, function(formatter, index){ + // format everything besides the defaultblock, which is already formated + return (!data.addDefaultblock || index+1 < data.addDefaultblock) + ? formatter(data.params[index]) + : data.params[index]; + }); + } - var isBigNumber = true; - if(!unitMap[unit]) { - console.warn('This unit doesn\'t exists, please use the one of the following units' , unitMap); - return number; - } + var payload = jsonrpc.toPayload(data.method, data.params); + + if (!provider) { + console.error('provider is not set'); + return null; + } - if(!number) - return number; + // ASYNC (only when callback is given, and it a HttpProvidor) + if(typeof callback === 'function' && provider.host){ + provider.send(payload, function(result){ - if(typeof number === 'string' && number.indexOf('0x') === 0) { - isBigNumber = false; - number = new BigNumber(number, 16); - } + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + if(typeof result === 'object' && result.error && result.error.message) + console.error(result.error.message); + return null; + } - if(!(number instanceof BigNumber)) { - isBigNumber = false; - number = new BigNumber(number.toString(10), 10);// toString to prevent errors, the user have to handle giving correct bignums themselves - } + // format the output + callback((typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result); + }); + // SYNC + } else { + var result = provider.send(payload); - number = number.times(new BigNumber(unitMap[unit], 10)); + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + if(typeof result === 'object' && result.error && result.error.message) + console.error(result.error.message); + return null; + } - return (isBigNumber) ? number : number.toString(10); -}; + // format the output + return (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result; + } + + }; + var setProvider = function (p) { + provider = p; + }; -/** -Checks if the given string is a valid ethereum HEX address. + /*jshint maxparams:4 */ + var startPolling = function (data, pollId, callback, uninstall) { + polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall}); + }; + /*jshint maxparams:3 */ -@method isAddress -@param {String} address the given HEX adress -@return {Boolean} -*/ -var isAddress = function(address) { - if(address.indexOf('0x') === 0 && address.length !== 42) - return false; - if(address.indexOf('0x') === -1 && address.length !== 40) - return false; + var stopPolling = function (pollId) { + for (var i = polls.length; i--;) { + var poll = polls[i]; + if (poll.id === pollId) { + polls.splice(i, 1); + } + } + }; - return /^\w+$/.test(address); -}; + var reset = function () { + polls.forEach(function (poll) { + poll.uninstall(poll.id); + }); + polls = []; -var isBigNumber = function (value) { - return value instanceof BigNumber || - (value && value.constructor && value.constructor.name === 'BigNumber'); -}; + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + poll(); + }; + var poll = function () { + polls.forEach(function (data) { + // send async + send(data.data, function(result){ + if (!(result instanceof Array) || result.length === 0) { + return; + } + data.callback(result); + }); + }); + timeout = setTimeout(poll, c.ETH_POLLING_TIMEOUT); + }; + + poll(); -module.exports = { - findIndex: findIndex, - toDecimal: toDecimal, - fromDecimal: fromDecimal, - toAscii: toAscii, - fromAscii: fromAscii, - extractDisplayName: extractDisplayName, - extractTypeName: extractTypeName, - filterFunctions: filterFunctions, - filterEvents: filterEvents, - toEth: toEth, - toWei: toWei, - fromWei: fromWei, - isAddress: isAddress, - isBigNumber: isBigNumber + return { + send: send, + setProvider: setProvider, + startPolling: startPolling, + stopPolling: stopPolling, + reset: reset + }; }; +module.exports = requestManager; + -},{"./const":2}],17:[function(require,module,exports){ +},{"../utils/config":4,"./jsonrpc":14}],18:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2002,41 +2626,34 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file watches.js +/** @file shh.js * @authors: * Marek Kotewicz * @date 2015 */ -/// @returns an array of objects describing web3.eth.filter api methods -var eth = function () { - var newFilter = function (args) { - return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; - }; +var formatters = require('./formatters'); +/// @returns an array of objects describing web3.shh api methods +var methods = function () { return [ - { name: 'newFilter', call: newFilter }, - { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, - { name: 'getLogs', call: 'eth_filterLogs' } - ]; -}; + { name: 'post', call: 'shh_post', inputFormatter: formatters.inputPostFormatter }, + { name: 'newIdentity', call: 'shh_newIdentity' }, + { name: 'hasIdentity', call: 'shh_hasIdentity' }, + { name: 'newGroup', call: 'shh_newGroup' }, + { name: 'addToGroup', call: 'shh_addToGroup' }, -/// @returns an array of objects describing web3.shh.watch api methods -var shh = function () { - return [ - { name: 'newFilter', call: 'shh_newFilter' }, - { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, - { name: 'getLogs', call: 'shh_getMessages' } + // deprecated + { name: 'haveIdentity', call: 'shh_haveIdentity', newMethod: 'shh.hasIdentity' }, ]; }; module.exports = { - eth: eth, - shh: shh + methods: methods }; -},{}],18:[function(require,module,exports){ +},{"./formatters":12}],19:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2053,242 +2670,94 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file web3.js +/** @file signature.js * @authors: - * Jeffrey Wilcke * Marek Kotewicz - * Marian Oancea - * Gav Wood - * @date 2014 + * @date 2015 */ -// if (process.env.NODE_ENV !== 'build') { -// var BigNumber = require('bignumber.js'); -// } +var web3 = require('../web3'); +var c = require('../utils/config'); -var eth = require('./eth'); -var db = require('./db'); -var shh = require('./shh'); -var watches = require('./watches'); -var filter = require('./filter'); -var utils = require('./utils'); -var requestManager = require('./requestmanager'); - -/// @returns an array of objects describing web3 api methods -var web3Methods = function () { - return [ - { name: 'sha3', call: 'web3_sha3' } - ]; +/// @param function name for which we want to get signature +/// @returns signature of function with given name +var functionSignatureFromAscii = function (name) { + return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2); }; -/// creates methods in a given object based on method description on input -/// setups api calls for these methods -var setupMethods = function (obj, methods) { - methods.forEach(function (method) { - // allow for object methods 'myObject.method' - var objectMethods = method.name.split('.'), - callFunction = function () { - var args = Array.prototype.slice.call(arguments); - var call = typeof method.call === 'function' ? method.call(args) : method.call; - - // show deprecated warning - if(method.newMethod) - console.warn('This method is deprecated please use eth.'+ method.newMethod +'() instead.'); - - return web3.manager.send({ - method: call, - params: args, - outputFormatter: method.outputFormatter, - inputFormatter: method.inputFormatter - }); - }; - - if(objectMethods.length > 1) { - if(!obj[objectMethods[0]]) - obj[objectMethods[0]] = {}; - - obj[objectMethods[0]][objectMethods[1]] = callFunction; - - } else { - - obj[objectMethods[0]] = callFunction; - } - - }); +/// @param event name for which we want to get signature +/// @returns signature of event with given name +var eventSignatureFromAscii = function (name) { + return web3.sha3(web3.fromAscii(name)); }; -/// creates properties in a given object based on properties description on input -/// setups api calls for these properties -var setupProperties = function (obj, properties) { - properties.forEach(function (property) { - var proto = {}; - proto.get = function () { - - // show deprecated warning - if(property.newProperty) - console.warn('This property is deprecated please use eth.'+ property.newProperty +' instead.'); - - - return web3.manager.send({ - method: property.getter, - outputFormatter: property.outputFormatter - }); - }; - - if (property.setter) { - proto.set = function (val) { +module.exports = { + functionSignatureFromAscii: functionSignatureFromAscii, + eventSignatureFromAscii: eventSignatureFromAscii +}; - // show deprecated warning - if(property.newProperty) - console.warn('This property is deprecated please use eth.'+ property.newProperty +' instead.'); - return web3.manager.send({ - method: property.setter, - params: [val], - inputFormatter: property.inputFormatter - }); - }; - } +},{"../utils/config":4,"../web3":6}],20:[function(require,module,exports){ +/* + This file is part of ethereum.js. - proto.enumerable = !property.newProperty; - Object.defineProperty(obj, property.name, proto); + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - }); -}; + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. -/*jshint maxparams:4 */ -var startPolling = function (method, id, callback, uninstall) { - web3.manager.startPolling({ - method: method, - params: [id] - }, id, callback, uninstall); -}; -/*jshint maxparams:3 */ + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file watches.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ -var stopPolling = function (id) { - web3.manager.stopPolling(id); -}; +/// @returns an array of objects describing web3.eth.filter api methods +var eth = function () { + var newFilter = function (args) { + return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter'; + }; -var ethWatch = { - startPolling: startPolling.bind(null, 'eth_changed'), - stopPolling: stopPolling + return [ + { name: 'newFilter', call: newFilter }, + { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, + { name: 'getLogs', call: 'eth_getFilterLogs' } + ]; }; -var shhWatch = { - startPolling: startPolling.bind(null, 'shh_changed'), - stopPolling: stopPolling +/// @returns an array of objects describing web3.shh.watch api methods +var shh = function () { + return [ + { name: 'newFilter', call: 'shh_newFilter' }, + { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, + { name: 'getLogs', call: 'shh_getMessages' } + ]; }; -/// setups web3 object, and it's in-browser executed methods -var web3 = { - manager: requestManager(), - providers: {}, - - setProvider: function (provider) { - web3.manager.setProvider(provider); - }, - - /// Should be called to reset state of web3 object - /// Resets everything except manager - reset: function () { - web3.manager.reset(); - }, - - /// @returns ascii string representation of hex value prefixed with 0x - toAscii: utils.toAscii, - - /// @returns hex representation (prefixed by 0x) of ascii string - fromAscii: utils.fromAscii, - - /// @returns decimal representaton of hex value prefixed by 0x - toDecimal: utils.toDecimal, - - /// @returns hex representation (prefixed by 0x) of decimal value - fromDecimal: utils.fromDecimal, - - /// used to transform value/string to eth string - toEth: utils.toEth, - - toWei: utils.toWei, - fromWei: utils.fromWei, - isAddress: utils.isAddress, - - - /// eth object prototype - eth: { - // DEPRECATED - contractFromAbi: function (abi) { - console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); - - return function(addr) { - // Default to address of Config. TODO: rremove prior to genesis. - addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; - var ret = web3.eth.contract(addr, abi); - ret.address = addr; - return ret; - }; - }, - - /// @param filter may be a string, object or event - /// @param eventParams is optional, this is an object with optional event eventParams params - /// @param options is optional, this is an object with optional event options ('max'...) - /// TODO: fix it, 4 params? no way - /*jshint maxparams:4 */ - filter: function (fil, eventParams, options, formatter) { - - // if its event, treat it differently - if (fil._isEvent) - return fil(eventParams, options); - - return filter(fil, ethWatch, formatter); - }, - // DEPRECATED - watch: function (fil, eventParams, options, formatter) { - console.warn('eth.watch() is deprecated please use eth.filter() instead.'); - return this.filter(fil, eventParams, options, formatter); - } - /*jshint maxparams:3 */ - }, - - /// db object prototype - db: {}, - - /// shh object prototype - shh: { - /// @param filter may be a string, object or event - filter: function (fil) { - return filter(fil, shhWatch); - }, - // DEPRECATED - watch: function (fil) { - console.warn('shh.watch() is deprecated please use shh.filter() instead.'); - return this.filter(fil); - } - } +module.exports = { + eth: eth, + shh: shh }; -/// setups all api methods -setupMethods(web3, web3Methods()); -setupMethods(web3.eth, eth.methods); -setupProperties(web3.eth, eth.properties); -setupMethods(web3.db, db.methods()); -setupMethods(web3.shh, shh.methods()); -setupMethods(ethWatch, watches.eth()); -setupMethods(shhWatch, watches.shh()); - -module.exports = web3; - -},{"./db":4,"./eth":5,"./filter":7,"./requestmanager":12,"./shh":13,"./utils":16,"./watches":17}],"web3":[function(require,module,exports){ +},{}],"web3":[function(require,module,exports){ var web3 = require('./lib/web3'); -web3.providers.HttpSyncProvider = require('./lib/httpsync'); -web3.providers.QtSyncProvider = require('./lib/qtsync'); -web3.eth.contract = require('./lib/contract'); -web3.abi = require('./lib/abi'); +web3.providers.HttpProvider = require('./lib/web3/httpprovider'); +web3.providers.QtSyncProvider = require('./lib/web3/qtsync'); +web3.eth.contract = require('./lib/web3/contract'); +web3.abi = require('./lib/solidity/abi'); module.exports = web3; -},{"./lib/abi":1,"./lib/contract":3,"./lib/httpsync":9,"./lib/qtsync":11,"./lib/web3":18}]},{},["web3"]) +},{"./lib/solidity/abi":1,"./lib/web3":6,"./lib/web3/contract":7,"./lib/web3/httpprovider":13,"./lib/web3/qtsync":16}]},{},["web3"]) //# sourceMappingURL=ethereum.js.map diff --git a/rpc/args.go b/rpc/args.go index 63969e598b..887d63d465 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -8,6 +8,30 @@ import ( "github.com/ethereum/go-ethereum/ethutil" ) +// Unmarshal state is a helper method which has the ability to decode messsages +// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter) +// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction +// message and the second one refers to the block height (or state) to which to apply this `call`. +func unmarshalState(b []byte, iface interface{}, str *string) (err error) { + var data []json.RawMessage + if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 { + return errDecodeArgs + } + + if err = json.Unmarshal(data[0], iface); err != nil { + return errDecodeArgs + } + + // Second argument is optional (transact doesn't require it) + if len(data) > 1 { + if err = json.Unmarshal(data[1], str); err != nil { + return errDecodeArgs + } + } + + return nil +} + type GetBlockByHashArgs struct { BlockHash string Transactions bool @@ -63,10 +87,12 @@ type NewTxArgs struct { Gas *big.Int GasPrice *big.Int Data string + + BlockHeight string } func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { - var obj []struct { + var obj struct { From string `json:"from"` To string `json:"to"` Value string `json:"value"` @@ -74,20 +100,18 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { GasPrice string `json:"gasPrice"` Data string `json:"data"` } - - if err = json.Unmarshal(b, &obj); err != nil { - return errDecodeArgs + var height string + if err = unmarshalState(b, &obj, &height); err != nil { + return err } - if len(obj) < 1 { - return errArguments - } - args.From = obj[0].From - args.To = obj[0].To - args.Value = ethutil.Big(obj[0].Value) - args.Gas = ethutil.Big(obj[0].Gas) - args.GasPrice = ethutil.Big(obj[0].GasPrice) - args.Data = obj[0].Data + args.From = obj.From + args.To = obj.To + args.Value = ethutil.Big(obj.Value) + args.Gas = ethutil.Big(obj.Gas) + args.GasPrice = ethutil.Big(obj.GasPrice) + args.Data = obj.Data + args.BlockHeight = height return nil }