diff --git a/lib/browser-xmlhttprequest.js b/lib/browser-xmlhttprequest.js new file mode 100644 index 000000000..efc222922 --- /dev/null +++ b/lib/browser-xmlhttprequest.js @@ -0,0 +1,10 @@ +'use strict'; + +try { + console.log('Starting load xml') + module.exports.XMLHttpRequest = XMLHttpRequest; + console.log('Done load xml') +} catch(error) { + console.log('Warning: XMLHttpRequest is not defined'); + module.exports.XMLHttpRequest = null; +} diff --git a/lib/contract.js b/lib/contract.js index 617fb7e1b..4b995b1bb 100644 --- a/lib/contract.js +++ b/lib/contract.js @@ -325,7 +325,7 @@ function Interface(abi) { result.parse = function(data) { return Interface.decodeParams( outputTypes, - utils.hexOrBuffer(data).toString('hex') + utils.hexOrBuffer(data) ) }; } else { @@ -358,7 +358,7 @@ function Interface(abi) { result.parse = function(data) { return Interface.decodeParams( inputTypes, - utils.hexOrBuffer(data).toString('hex') + utils.hexOrBuffer(data) ); }; return result; @@ -434,7 +434,6 @@ utils.defineProperty(Interface, 'decodeParams', function(types, data) { data = utils.hexOrBuffer(data); var values = []; - var offset = 0; types.forEach(function(type) { var coder = getParamCoder(type); @@ -448,7 +447,6 @@ utils.defineProperty(Interface, 'decodeParams', function(types, data) { } values.push(result.value); }); - return values; }); @@ -457,13 +455,14 @@ var allowedTransactionKeys = { data: true, from: true, gasLimit: true, gasPrice:true, to: true, value: true } -function Contract(web3, wallet, contractAddress, contractInterface) { - utils.defineProperty(this, 'web3', web3); +function Contract(provider, wallet, contractAddress, contractInterface) { + utils.defineProperty(this, 'provider', provider); utils.defineProperty(this, 'wallet', wallet); + utils.defineProperty(this, 'contractAddress', contractAddress); utils.defineProperty(this, 'interface', contractInterface); - +/* function getWeb3Promise(method) { var params = Array.prototype.slice.call(arguments, 1); return new Promise(function(resolve, reject) { @@ -476,7 +475,7 @@ function Contract(web3, wallet, contractAddress, contractInterface) { web3.eth[method].apply(web3, params); }); } - +*/ var self = this; var filters = {}; @@ -504,6 +503,7 @@ function Contract(web3, wallet, contractAddress, contractInterface) { filters[call.name] = info; // Start a new filter +/* info.filter = web3.eth.filter({ address: contractAddress, topics: call.topics @@ -520,8 +520,8 @@ function Contract(web3, wallet, contractAddress, contractInterface) { console.log(error); } }); +*/ } - function runMethod(method) { return function() { var transaction = {} @@ -539,6 +539,7 @@ function Contract(web3, wallet, contractAddress, contractInterface) { } } + var call = contractInterface[method].apply(contractInterface, params); switch (call.type) { case 'call': @@ -554,7 +555,7 @@ function Contract(web3, wallet, contractAddress, contractInterface) { transaction.to = contractAddress; return new Promise(function(resolve, reject) { - getWeb3Promise('call', transaction).then(function(value) { + provider.client.sendMessage('eth_call', [transaction]).then(function(value) { resolve(call.parse(value)); }, function(error) { reject(error); @@ -575,8 +576,8 @@ function Contract(web3, wallet, contractAddress, contractInterface) { return new Promise(function(resolve, reject) { Promise.all([ - getWeb3Promise('getTransactionCount', wallet.address, 'pending'), - getWeb3Promise('getGasPrice'), + provider.client.sendMessage('getTransactionCount', [wallet.address, 'pending']), + provider.client.sendMessage('getGasPrice', []), ]).then(function(results) { if (transaction.nonce == null) { transaction.nonce = results[0]; @@ -588,16 +589,9 @@ function Contract(web3, wallet, contractAddress, contractInterface) { } else if (console.warn) { console.warn('Overriding suggested gasPrice: ' + utils.hexlify(results[1])); } + var signedTransaction = wallet.sign(transaction); - /* - data: call.data, - gasLimit: 3000000, - gasPrice: results[1], - nonce: results[0], - to: contractAddress, - }); - */ - getWeb3Promise('sendRawTransaction', signedTransaction).then(function(txid) { + provider.client.sendMessage('sendRawTransaction', [signedTransaction]).then(function(txid) { resolve(txid); }, function(error) { reject(error); @@ -619,13 +613,13 @@ function Contract(web3, wallet, contractAddress, contractInterface) { Object.defineProperty(self, 'on' + call.name.toLowerCase(), { enumerable: true, get: function() { - console.log('get'); + //console.log('get'); var info = filters[call.name]; if (!info || !info[call.name]) { return null; } return info.callback; }, set: function(value) { - console.log('set'); + //console.log('set'); setupFilter(call, value); } }); diff --git a/lib/provider.js b/lib/provider.js new file mode 100644 index 000000000..9b05a6196 --- /dev/null +++ b/lib/provider.js @@ -0,0 +1,102 @@ +'use strict'; + +var utils = require('./utils.js'); +var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; + + +function Web3Connector(provider) { + if (!(this instanceof Web3Connector)) { throw new Error('missing new'); } + + var nextMessageId = 1; + utils.defineProperty(this, 'sendMessage', function(method, params) { + //console.log('mm', method, params); + return new Promise(function(resolve, reject) { + provider.sendAsync({ + id: (nextMessageId++), + jsonrpc: '2.0', + method: method, + params: params + }, function(error, result) { + console.log(error, result); + if (error) { + reject(error); + } else { + if (result.code) { + var error = new Error(result.message); + error.code = result.code; + error.data = result.data; + reject(error); + } else { + resolve(result.result); + } + } + }); + }); + }); +} + + +function rpcSendAsync(url) { + return { + sendAsync: function(payload, callback) { + + var request = new XMLHttpRequest(); + request.open('POST', url, true); + request.setRequestHeader('Content-Type','application/json'); + request.onreadystatechange = function() { + if (request.readyState !== 4) { return; } + + if (typeof(callback) !== 'function') { return; } + + var result = request.responseText; + try { + callback(null, JSON.parse(result)); + } catch (error) { + callback(new Error('invalid response')); + } + }; + + try { + request.send(JSON.stringify(payload)); + } catch (error) { + var connectionError = new Error('connection error'); + connectionError.error = error; + callback(connectionError); + } + } + } +} + + +function Provider(provider) { + if (!(this instanceof Provider)) { throw new Error('missing new'); } + + var client = null; + + if (typeof(provider) === 'string') { + + // An RPC URL + if (provider.substring(0, 7) === 'http://') { + client = new Web3Connector(rpcSendAsync(provider)); + + // An ethers.io URL + } else if (provider.substring(0, 5) === 'ws://' || provider.substirng(0, 6) === 'wss://') { + //client = + } + + // A Web3 Instance + } else if (provider.currentProvider && provider.currentProvider.sendAsync) { + client = new Web3Connector(provider.currentProvider); + + // A Web3 Provider + } else if (provider.sendAsync) { + client = new Web3Connector(provider); + } + + if (!client) { throw new Error('invalid connector'); } + + utils.defineProperty(this, 'client', client); +} + + +module.exports = Provider; diff --git a/lib/wallet.js b/lib/wallet.js index e29271d5f..a1b556d8f 100644 --- a/lib/wallet.js +++ b/lib/wallet.js @@ -3,6 +3,7 @@ var rlp = require('rlp'); var Contract = require('./contract.js'); +var Provider = require('./provider.js'); var SigningKey = require('./signing-key.js'); var utils = require('./utils.js'); @@ -17,7 +18,7 @@ var transactionFields = [ {name: 'data'}, ]; -function Wallet(privateKey) { +function Wallet(privateKey, provider) { if (!(this instanceof Wallet)) { throw new Error('missing new'); } // Make sure we have a valid signing key @@ -27,6 +28,10 @@ function Wallet(privateKey) { } utils.defineProperty(this, 'privateKey', signingKey.privateKey); + if (provider) { + utils.defineProperty(this, 'provider', new Provider(provider)); + } + utils.defineProperty(this, 'address', signingKey.address); utils.defineProperty(this, 'sign', function(transaction) { @@ -71,14 +76,15 @@ function Wallet(privateKey) { }); } +utils.defineProperty(Wallet.prototype, 'getContract', function(address, abi) { + return new Contract(this.provider, this, address, new Contract.Interface(abi)); +}); + + utils.defineProperty(Wallet, 'getAddress', SigningKey.getAddress); utils.defineProperty(Wallet, 'getIcapAddress', SigningKey.getIcapAddress); utils.defineProperty(Wallet, '_Contract', Contract); -utils.defineProperty(Wallet.prototype, 'getContract', function(address, abi, web3) { - return new Contract(web3, this, address, new Contract.Interface(abi)); -}); - module.exports = Wallet; diff --git a/package.json b/package.json index 7171c2cfb..7ac159b62 100644 --- a/package.json +++ b/package.json @@ -4,16 +4,21 @@ "description": "Ethereum wallet library.", "main": "index.js", "scripts": { - "test": "./node_modules/.bin/nodeunit test.js", + "test": "./node_modules/.bin/nodeunit tests/index.js", "version": "grunt dist" }, "dependencies": { "aes-js": "0.2.4", "elliptic": "6.3.1", "pbkdf2": "3.0.4", + "randombytes": "2.0.3", "rlp": "2.0.0", "scrypt-js": "2.0.0", - "uuid": "2.0.1" + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "browser": { + "xmlhttprequest": "./lib/browser/browser-xmlhttprequest.js" }, "devDependencies": { "ethereumjs-abi": "0.6.2",