diff --git a/Cargo.lock b/Cargo.lock index e9f54c918..08edcc166 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1503,7 +1503,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#d95a7dd2cc7469dc58af77743ec3ebc65e51cf36" +source = "git+https://github.com/ethcore/js-precompiled.git#ebea2bf78e076916b51b04d8b24187a6a85ae440" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 1226a5abe..62e9af250 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -378,7 +378,8 @@ impl BlockProvider for BlockChain { .enumerate() .flat_map(move |(index, (mut logs, tx_hash))| { let current_log_index = log_index; - log_index -= logs.len(); + let no_of_logs = logs.len(); + log_index -= no_of_logs; logs.reverse(); logs.into_iter() @@ -390,6 +391,7 @@ impl BlockProvider for BlockChain { transaction_hash: tx_hash, // iterating in reverse order transaction_index: receipts_len - index - 1, + transaction_log_index: no_of_logs - i - 1, log_index: current_log_index - i - 1, }) }) @@ -1936,6 +1938,7 @@ mod tests { block_number: block1.header().number(), transaction_hash: tx_hash1.clone(), transaction_index: 0, + transaction_log_index: 0, log_index: 0, }, LocalizedLogEntry { @@ -1944,6 +1947,7 @@ mod tests { block_number: block1.header().number(), transaction_hash: tx_hash1.clone(), transaction_index: 0, + transaction_log_index: 1, log_index: 1, }, LocalizedLogEntry { @@ -1952,6 +1956,7 @@ mod tests { block_number: block1.header().number(), transaction_hash: tx_hash2.clone(), transaction_index: 1, + transaction_log_index: 0, log_index: 2, }, LocalizedLogEntry { @@ -1960,6 +1965,7 @@ mod tests { block_number: block2.header().number(), transaction_hash: tx_hash3.clone(), transaction_index: 0, + transaction_log_index: 0, log_index: 0, } ]); @@ -1970,6 +1976,7 @@ mod tests { block_number: block2.header().number(), transaction_hash: tx_hash3.clone(), transaction_index: 0, + transaction_log_index: 0, log_index: 0, } ]); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 48ff03095..f57b1248a 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -59,7 +59,7 @@ use client::{ use client::Error as ClientError; use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; -use receipt::LocalizedReceipt; +use receipt::{Receipt, LocalizedReceipt}; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace; use trace::FlatTransactionTraces; @@ -837,7 +837,6 @@ impl snapshot::DatabaseRestore for Client { } } - impl BlockChainClient for Client { fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result { let header = self.block_header(block).ok_or(CallError::StatePruned)?; @@ -1134,53 +1133,23 @@ impl BlockChainClient for Client { let chain = self.chain.read(); self.transaction_address(id) .and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| { - let t = chain.block_body(&address.block_hash) - .and_then(|body| { - body.view().localized_transaction_at(&address.block_hash, block_number, address.index) - }); + let transaction = chain.block_body(&address.block_hash) + .and_then(|body| body.view().localized_transaction_at(&address.block_hash, block_number, address.index)); - let tx_and_sender = t.and_then(|tx| tx.sender().ok().map(|sender| (tx, sender))); - - match (tx_and_sender, chain.transaction_receipt(&address)) { - (Some((tx, sender)), Some(receipt)) => { - let block_hash = tx.block_hash.clone(); - let block_number = tx.block_number.clone(); - let transaction_hash = tx.hash(); - let transaction_index = tx.transaction_index; - let prior_gas_used = match tx.transaction_index { - 0 => U256::zero(), - i => { - let prior_address = TransactionAddress { block_hash: address.block_hash, index: i - 1 }; - let prior_receipt = chain.transaction_receipt(&prior_address).expect("Transaction receipt at `address` exists; `prior_address` has lower index in same block; qed"); - prior_receipt.gas_used - } - }; - Some(LocalizedReceipt { - transaction_hash: tx.hash(), - transaction_index: tx.transaction_index, - block_hash: tx.block_hash, - block_number: tx.block_number, - cumulative_gas_used: receipt.gas_used, - gas_used: receipt.gas_used - prior_gas_used, - contract_address: match tx.action { - Action::Call(_) => None, - Action::Create => Some(contract_address(&sender, &tx.nonce)) - }, - logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry { - entry: log, - block_hash: block_hash.clone(), - block_number: block_number, - transaction_hash: transaction_hash.clone(), - transaction_index: transaction_index, - log_index: i - }).collect(), - log_bloom: receipt.log_bloom, - state_root: receipt.state_root, + let previous_receipts = (0..address.index + 1) + .map(|index| { + let mut address = address.clone(); + address.index = index; + chain.transaction_receipt(&address) }) - }, - _ => None - } - })) + .collect(); + match (transaction, previous_receipts) { + (Some(transaction), Some(previous_receipts)) => { + Some(transaction_receipt(transaction, previous_receipts)) + }, + _ => None, + } + })) } fn tree_route(&self, from: &H256, to: &H256) -> Option { @@ -1535,6 +1504,49 @@ impl Drop for Client { } } +/// Returns `LocalizedReceipt` given `LocalizedTransaction` +/// and a vector of receipts from given block up to transaction index. +fn transaction_receipt(tx: LocalizedTransaction, mut receipts: Vec) -> LocalizedReceipt { + assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided."); + + let sender = tx.sender() + .expect("LocalizedTransaction is part of the blockchain; We have only valid transactions in chain; qed"); + let receipt = receipts.pop().expect("Current receipt is provided; qed"); + let prior_gas_used = match tx.transaction_index { + 0 => 0.into(), + i => receipts.get(i - 1).expect("All previous receipts are provided; qed").gas_used, + }; + let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::(); + let transaction_hash = tx.hash(); + let block_hash = tx.block_hash; + let block_number = tx.block_number; + let transaction_index = tx.transaction_index; + + LocalizedReceipt { + transaction_hash: transaction_hash, + transaction_index: transaction_index, + block_hash: block_hash, + block_number:block_number, + cumulative_gas_used: receipt.gas_used, + gas_used: receipt.gas_used - prior_gas_used, + contract_address: match tx.action { + Action::Call(_) => None, + Action::Create => Some(contract_address(&sender, &tx.nonce)) + }, + logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry { + entry: log, + block_hash: block_hash, + block_number: block_number, + transaction_hash: transaction_hash, + transaction_index: transaction_index, + transaction_log_index: i, + log_index: no_of_logs + i, + }).collect(), + log_bloom: receipt.log_bloom, + state_root: receipt.state_root, + } +} + #[cfg(test)] mod tests { @@ -1570,4 +1582,91 @@ mod tests { assert!(client.tree_route(&genesis, &new_hash).is_none()); } + + #[test] + fn should_return_correct_log_index() { + use super::transaction_receipt; + use ethkey::KeyPair; + use log_entry::{LogEntry, LocalizedLogEntry}; + use receipt::{Receipt, LocalizedReceipt}; + use transaction::{Transaction, LocalizedTransaction, Action}; + use util::Hashable; + + // given + let key = KeyPair::from_secret("test".sha3()).unwrap(); + let secret = key.secret(); + + let block_number = 1; + let block_hash = 5.into(); + let state_root = 99.into(); + let gas_used = 10.into(); + let raw_tx = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(10.into()), + value: 0.into(), + data: vec![], + }; + let tx1 = raw_tx.clone().sign(secret, None); + let transaction = LocalizedTransaction { + signed: tx1.clone(), + block_number: block_number, + block_hash: block_hash, + transaction_index: 1, + }; + let logs = vec![LogEntry { + address: 5.into(), + topics: vec![], + data: vec![], + }, LogEntry { + address: 15.into(), + topics: vec![], + data: vec![], + }]; + let receipts = vec![Receipt { + state_root: state_root, + gas_used: 5.into(), + log_bloom: Default::default(), + logs: vec![logs[0].clone()], + }, Receipt { + state_root: state_root, + gas_used: gas_used, + log_bloom: Default::default(), + logs: logs.clone(), + }]; + + // when + let receipt = transaction_receipt(transaction, receipts); + + // then + assert_eq!(receipt, LocalizedReceipt { + transaction_hash: tx1.hash(), + transaction_index: 1, + block_hash: block_hash, + block_number: block_number, + cumulative_gas_used: gas_used, + gas_used: gas_used - 5.into(), + contract_address: None, + logs: vec![LocalizedLogEntry { + entry: logs[0].clone(), + block_hash: block_hash, + block_number: block_number, + transaction_hash: tx1.hash(), + transaction_index: 1, + transaction_log_index: 0, + log_index: 1, + }, LocalizedLogEntry { + entry: logs[1].clone(), + block_hash: block_hash, + block_number: block_number, + transaction_hash: tx1.hash(), + transaction_index: 1, + transaction_log_index: 1, + log_index: 2, + }], + log_bloom: Default::default(), + state_root: state_root, + }); + } } diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index c6a9d8157..0dfd8434d 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -320,4 +320,3 @@ fn does_not_propagate_delayed_transactions() { assert_eq!(2, client.ready_transactions().len()); assert_eq!(2, client.miner().pending_transactions().len()); } - diff --git a/ethcore/src/types/log_entry.rs b/ethcore/src/types/log_entry.rs index 1fbb4305f..79b93b04e 100644 --- a/ethcore/src/types/log_entry.rs +++ b/ethcore/src/types/log_entry.rs @@ -97,6 +97,8 @@ pub struct LocalizedLogEntry { pub transaction_index: usize, /// Log position in the block. pub log_index: usize, + /// Log position in the transaction. + pub transaction_log_index: usize, } impl Deref for LocalizedLogEntry { diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 77a6a99a9..4bd34da18 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -373,7 +373,7 @@ impl SignedTransaction { } /// Signed Transaction that is a part of canon blockchain. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "ipc", binary)] pub struct LocalizedTransaction { /// Signed part. diff --git a/js/package.json b/js/package.json index 242676eb0..2ecad7269 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.152", + "version": "0.2.165", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", @@ -26,9 +26,9 @@ ], "scripts": { "build": "npm run build:lib && npm run build:dll && npm run build:app", - "build:app": "webpack --config webpack/app --progress", - "build:lib": "webpack --config webpack/libraries --progress", - "build:dll": "webpack --config webpack/vendor --progress", + "build:app": "webpack --config webpack/app", + "build:lib": "webpack --config webpack/libraries", + "build:dll": "webpack --config webpack/vendor", "ci:build": "npm run ci:build:lib && npm run ci:build:dll && npm run ci:build:app", "ci:build:app": "NODE_ENV=production webpack --config webpack/app", "ci:build:lib": "NODE_ENV=production webpack --config webpack/libraries", @@ -51,19 +51,19 @@ }, "devDependencies": { "babel-cli": "6.18.0", - "babel-core": "6.20.0", + "babel-core": "6.21.0", "babel-eslint": "7.1.1", "babel-loader": "6.2.10", - "babel-plugin-lodash": "3.2.10", + "babel-plugin-lodash": "3.2.11", "babel-plugin-react-intl": "2.2.0", - "babel-plugin-transform-class-properties": "6.18.0", + "babel-plugin-transform-class-properties": "6.19.0", "babel-plugin-transform-decorators-legacy": "1.3.4", "babel-plugin-transform-object-rest-spread": "6.20.2", "babel-plugin-transform-react-remove-prop-types": "0.2.11", "babel-plugin-transform-runtime": "6.15.0", "babel-plugin-webpack-alias": "2.1.2", "babel-polyfill": "6.20.0", - "babel-preset-env": "1.0.2", + "babel-preset-env": "1.1.4", "babel-preset-es2015": "6.18.0", "babel-preset-es2016": "6.16.0", "babel-preset-es2017": "6.16.0", @@ -80,57 +80,58 @@ "coveralls": "2.11.15", "css-loader": "0.26.1", "ejs-loader": "0.3.0", - "enzyme": "2.6.0", + "enzyme": "2.7.0", "eslint": "3.11.1", "eslint-config-semistandard": "7.0.0", "eslint-config-standard": "6.2.1", "eslint-config-standard-react": "4.2.0", "eslint-plugin-promise": "3.4.0", - "eslint-plugin-react": "6.7.1", + "eslint-plugin-react": "6.8.0", "eslint-plugin-standard": "2.0.1", "express": "4.14.0", "extract-loader": "0.1.0", "extract-text-webpack-plugin": "2.0.0-beta.4", "file-loader": "0.9.0", - "happypack": "3.0.0", + "happypack": "3.0.2", "html-loader": "0.4.4", "html-webpack-plugin": "2.24.1", - "http-proxy-middleware": "0.17.2", + "http-proxy-middleware": "0.17.3", "husky": "0.11.9", "ignore-styles": "5.0.1", - "image-webpack-loader": "3.0.0", + "image-webpack-loader": "3.1.0", "istanbul": "1.0.0-alpha.2", - "jsdom": "9.8.3", + "jsdom": "9.9.1", "json-loader": "0.5.4", "mocha": "3.2.0", "mock-local-storage": "1.0.2", - "mock-socket": "6.0.3", + "mock-socket": "6.0.4", "nock": "9.0.2", "postcss-import": "9.0.0", - "postcss-loader": "1.2.0", + "postcss-loader": "1.2.1", "postcss-nested": "1.0.0", "postcss-simple-vars": "3.0.0", "progress": "1.1.8", + "progress-bar-webpack-plugin": "1.9.1", "raw-loader": "0.5.1", "react-addons-perf": "15.4.1", "react-addons-test-utils": "15.4.1", "react-hot-loader": "3.0.0-beta.6", "react-intl-aggregate-webpack-plugin": "0.0.1", "rucksack-css": "0.9.1", - "script-ext-html-webpack-plugin": "1.3.4", + "script-ext-html-webpack-plugin": "1.3.5", "serviceworker-webpack-plugin": "0.1.7", "sinon": "1.17.6", "sinon-as-promised": "4.0.2", "sinon-chai": "2.8.0", "style-loader": "0.13.1", - "stylelint": "7.6.0", - "stylelint-config-standard": "15.0.0", + "stylelint": "7.7.0", + "stylelint-config-standard": "15.0.1", "url-loader": "0.5.7", "webpack": "2.2.0-rc.2", - "webpack-dev-middleware": "1.8.4", + "webpack-dev-middleware": "1.9.0", "webpack-error-notification": "0.1.6", - "webpack-hot-middleware": "2.13.2", - "websocket": "1.0.23" + "webpack-hot-middleware": "2.14.0", + "websocket": "1.0.24" }, "dependencies": { "bignumber.js": "3.0.1", diff --git a/js/src/abi/encoder/encoder.js b/js/src/abi/encoder/encoder.js index 8634b9511..6797e978d 100644 --- a/js/src/abi/encoder/encoder.js +++ b/js/src/abi/encoder/encoder.js @@ -25,7 +25,7 @@ export default class Encoder { throw new Error('tokens should be array of Token'); } - const mediates = tokens.map((token) => Encoder.encodeToken(token)); + const mediates = tokens.map((token, index) => Encoder.encodeToken(token, index)); const inits = mediates .map((mediate, idx) => mediate.init(Mediate.offsetFor(mediates, idx))) .join(''); @@ -36,37 +36,40 @@ export default class Encoder { return `${inits}${closings}`; } - static encodeToken (token) { + static encodeToken (token, index = 0) { if (!isInstanceOf(token, Token)) { throw new Error('token should be instanceof Token'); } - switch (token.type) { - case 'address': - return new Mediate('raw', padAddress(token.value)); + try { + switch (token.type) { + case 'address': + return new Mediate('raw', padAddress(token.value)); - case 'int': - case 'uint': - return new Mediate('raw', padU32(token.value)); + case 'int': + case 'uint': + return new Mediate('raw', padU32(token.value)); - case 'bool': - return new Mediate('raw', padBool(token.value)); + case 'bool': + return new Mediate('raw', padBool(token.value)); - case 'fixedBytes': - return new Mediate('raw', padFixedBytes(token.value)); + case 'fixedBytes': + return new Mediate('raw', padFixedBytes(token.value)); - case 'bytes': - return new Mediate('prefixed', padBytes(token.value)); + case 'bytes': + return new Mediate('prefixed', padBytes(token.value)); - case 'string': - return new Mediate('prefixed', padString(token.value)); + case 'string': + return new Mediate('prefixed', padString(token.value)); - case 'fixedArray': - case 'array': - return new Mediate(token.type, token.value.map((token) => Encoder.encodeToken(token))); - - default: - throw new Error(`Invalid token type ${token.type} in encodeToken`); + case 'fixedArray': + case 'array': + return new Mediate(token.type, token.value.map((token) => Encoder.encodeToken(token))); + } + } catch (e) { + throw new Error(`Cannot encode token #${index} [${token.type}: ${token.value}]. ${e.message}`); } + + throw new Error(`Invalid token type ${token.type} in encodeToken`); } } diff --git a/js/src/abi/spec/interface.js b/js/src/abi/spec/interface.js index 9116f5ca3..3e1b5de4d 100644 --- a/js/src/abi/spec/interface.js +++ b/js/src/abi/spec/interface.js @@ -41,6 +41,10 @@ export default class Interface { } encodeTokens (paramTypes, values) { + return Interface.encodeTokens(paramTypes, values); + } + + static encodeTokens (paramTypes, values) { const createToken = function (paramType, value) { if (paramType.subtype) { return new Token(paramType.type, value.map((entry) => createToken(paramType.subtype, entry))); diff --git a/js/src/api/api.js b/js/src/api/api.js index 9e0ad11b8..bb622ab46 100644 --- a/js/src/api/api.js +++ b/js/src/api/api.js @@ -114,7 +114,11 @@ export default class Api { } }) .catch((error) => { - console.error('pollMethod', error); + // Don't print if the request is rejected: that's ok + if (error.type !== 'REQUEST_REJECTED') { + console.error('pollMethod', error); + } + reject(error); }); }; diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index eacb21fd1..af22191e5 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import Abi from '../../abi'; +import Abi from '~/abi'; let nextSubscriptionId = 0; @@ -53,6 +53,10 @@ export default class Contract { this._subscribedToBlock = false; this._blockSubscriptionId = null; + + if (api && api.patch && api.patch.contract) { + api.patch.contract(this); + } } get address () { @@ -90,8 +94,10 @@ export default class Contract { } deployEstimateGas (options, values) { + const _options = this._encodeOptions(this.constructors[0], options, values); + return this._api.eth - .estimateGas(this._encodeOptions(this.constructors[0], options, values)) + .estimateGas(_options) .then((gasEst) => { return [gasEst, gasEst.mul(1.2)]; }); @@ -115,8 +121,10 @@ export default class Contract { setState({ state: 'postTransaction', gas }); + const _options = this._encodeOptions(this.constructors[0], options, values); + return this._api.parity - .postTransaction(this._encodeOptions(this.constructors[0], options, values)) + .postTransaction(_options) .then((requestId) => { setState({ state: 'checkRequest', requestId }); return this._pollCheckRequest(requestId); @@ -199,7 +207,7 @@ export default class Contract { getCallData = (func, options, values) => { let data = options.data; - const tokens = func ? this._abi.encodeTokens(func.inputParamTypes(), values) : null; + const tokens = func ? Abi.encodeTokens(func.inputParamTypes(), values) : null; const call = tokens ? func.encodeCall(tokens) : null; if (data && data.substr(0, 2) === '0x') { @@ -221,6 +229,8 @@ export default class Contract { } _bindFunction = (func) => { + func.contract = this; + func.call = (options, values = []) => { const callParams = this._encodeOptions(func, this._addOptionsTo(options), values); @@ -233,13 +243,13 @@ export default class Contract { if (!func.constant) { func.postTransaction = (options, values = []) => { - return this._api.parity - .postTransaction(this._encodeOptions(func, this._addOptionsTo(options), values)); + const _options = this._encodeOptions(func, this._addOptionsTo(options), values); + return this._api.parity.postTransaction(_options); }; func.estimateGas = (options, values = []) => { - return this._api.eth - .estimateGas(this._encodeOptions(func, this._addOptionsTo(options), values)); + const _options = this._encodeOptions(func, this._addOptionsTo(options), values); + return this._api.eth.estimateGas(_options); }; } diff --git a/js/src/api/transport/ws/ws.js b/js/src/api/transport/ws/ws.js index 43f1403ce..591cf3062 100644 --- a/js/src/api/transport/ws/ws.js +++ b/js/src/api/transport/ws/ws.js @@ -209,7 +209,10 @@ export default class Ws extends JsonRpcBase { if (result.error) { this.error(event.data); - console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`); + // Don't print error if request rejected... + if (!/rejected/.test(result.error.message)) { + console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`); + } const error = new TransportError(method, result.error.code, result.error.message); reject(error); diff --git a/js/src/api/util/decode.js b/js/src/api/util/decode.js index a6a2ddda4..0e0164bec 100644 --- a/js/src/api/util/decode.js +++ b/js/src/api/util/decode.js @@ -47,8 +47,6 @@ export function decodeMethodInput (methodAbi, paramdata) { throw new Error('Input to decodeMethodInput should be a hex value'); } else if (paramdata.substr(0, 2) === '0x') { return decodeMethodInput(methodAbi, paramdata.slice(2)); - } else if (paramdata.length % 64 !== 0) { - throw new Error('Parameter length in decodeMethodInput not a multiple of 64 characters'); } } diff --git a/js/src/api/util/decode.spec.js b/js/src/api/util/decode.spec.js index 4652c7c5b..fa0102365 100644 --- a/js/src/api/util/decode.spec.js +++ b/js/src/api/util/decode.spec.js @@ -48,10 +48,6 @@ describe('api/util/decode', () => { expect(() => decodeMethodInput({}, 'invalid')).to.throw(/should be a hex value/); }); - it('throws on invalid lengths', () => { - expect(() => decodeMethodInput({}, DATA.slice(-32))).to.throw(/not a multiple of/); - }); - it('correctly decodes valid inputs', () => { expect(decodeMethodInput({ type: 'function', diff --git a/js/src/index.js b/js/src/index.js index 77f247b3b..72f26e12f 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -36,6 +36,7 @@ import ContextProvider from '~/ui/ContextProvider'; import muiTheme from '~/ui/Theme'; import MainApplication from './main'; +import { patchApi } from '~/util/tx'; import { setApi } from '~/redux/providers/apiActions'; import './environment'; @@ -60,6 +61,7 @@ if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) { } const api = new SecureApi(`ws://${parityUrl}`, token); +patchApi(api); ContractInstances.create(api); const store = initStore(api, hashHistory); diff --git a/js/src/modals/AddContract/addContract.js b/js/src/modals/AddContract/addContract.js index 0d04438c3..49d3a4143 100644 --- a/js/src/modals/AddContract/addContract.js +++ b/js/src/modals/AddContract/addContract.js @@ -14,264 +14,255 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; -import ContentAdd from 'material-ui/svg-icons/content/add'; -import ContentClear from 'material-ui/svg-icons/content/clear'; -import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward'; -import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back'; +import { FormattedMessage } from 'react-intl'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { newError } from '~/redux/actions'; import { Button, Modal, Form, Input, InputAddress, RadioButtons } from '~/ui'; -import { ERRORS, validateAbi, validateAddress, validateName } from '~/util/validation'; +import { AddIcon, CancelIcon, NextIcon, PrevIcon } from '~/ui/Icons'; -import { eip20, wallet } from '~/contracts/abi'; +import Store from './store'; -const ABI_TYPES = [ - { - label: 'Token', readOnly: true, value: JSON.stringify(eip20), - type: 'token', - description: (A standard ERC 20 token) - }, - { - label: 'Multisig Wallet', readOnly: true, - type: 'multisig', - value: JSON.stringify(wallet), - description: (Official Multisig contract: see contract code) - }, - { - label: 'Custom Contract', value: '', - type: 'custom', - description: 'Contract created from custom ABI' - } -]; - -const STEPS = [ 'choose a contract type', 'enter contract details' ]; - -export default class AddContract extends Component { +@observer +class AddContract extends Component { static contextTypes = { api: PropTypes.object.isRequired } static propTypes = { contracts: PropTypes.object.isRequired, + newError: PropTypes.func.isRequired, onClose: PropTypes.func }; - state = { - abi: '', - abiError: ERRORS.invalidAbi, - abiType: ABI_TYPES[2], - abiTypeIndex: 2, - abiParsed: null, - address: '', - addressError: ERRORS.invalidAddress, - name: '', - nameError: ERRORS.invalidName, - description: '', - step: 0 - }; - - componentDidMount () { - this.onChangeABIType(null, this.state.abiTypeIndex); - } + store = new Store(this.context.api, this.props.contracts); render () { - const { step } = this.state; + const { step } = this.store; return ( - { this.renderStep(step) } + steps={ [ + , + + ] } + visible> + { this.renderStep() } ); } - renderStep (step) { + renderStep () { + const { step } = this.store; + switch (step) { case 0: return this.renderContractTypeSelector(); + default: return this.renderFields(); } } renderContractTypeSelector () { - const { abiTypeIndex } = this.state; + const { abiTypeIndex, abiTypes } = this.store; return ( ); } renderDialogActions () { - const { addressError, nameError, step } = this.state; - const hasError = !!(addressError || nameError); + const { step } = this.store; const cancelBtn = (