diff --git a/js/.eslintrc.json b/js/.eslintrc.json index 42f9b0554..ffd4f03f3 100644 --- a/js/.eslintrc.json +++ b/js/.eslintrc.json @@ -13,6 +13,7 @@ "rules": { "curly": ["error", "all"], "jsx-quotes": ["error", "prefer-single"], + "newline-after-var": ["error", "always"], "no-alert": "error", "no-debugger": "error", "no-duplicate-imports": ["error", { @@ -20,6 +21,7 @@ }], "object-curly-spacing": ["error", "always"], "object-property-newline": 0, + "one-var-declaration-per-line": ["error", "always"], "padded-blocks": ["error", { "blocks": "never", "classes": "never", diff --git a/js/src/3rdparty/email-verification/index.js b/js/src/3rdparty/email-verification/index.js index b1a868b0b..ea762fb5b 100644 --- a/js/src/3rdparty/email-verification/index.js +++ b/js/src/3rdparty/email-verification/index.js @@ -49,6 +49,7 @@ export const hasReceivedCode = (email, address, isTestnet = false) => { export const postToServer = (query, isTestnet = false) => { const port = isTestnet ? 28443 : 18443; + query = stringify(query); return fetch(`https://email-verification.parity.io:${port}/?${query}`, { diff --git a/js/src/3rdparty/sms-verification/index.js b/js/src/3rdparty/sms-verification/index.js index 3fd3b0512..ba7563c35 100644 --- a/js/src/3rdparty/sms-verification/index.js +++ b/js/src/3rdparty/sms-verification/index.js @@ -49,6 +49,7 @@ export const hasReceivedCode = (number, address, isTestnet = false) => { export const postToServer = (query, isTestnet = false) => { const port = isTestnet ? 8443 : 443; + query = stringify(query); return fetch(`https://sms-verification.parity.io:${port}/?${query}`, { diff --git a/js/src/abi/decoder/decoder.js b/js/src/abi/decoder/decoder.js index c1cac6034..1a81c591b 100644 --- a/js/src/abi/decoder/decoder.js +++ b/js/src/abi/decoder/decoder.js @@ -37,6 +37,7 @@ export default class Decoder { return params.map((param) => { const result = Decoder.decodeParam(param, slices, offset); + offset = result.newOffset; return result.token; }); @@ -121,6 +122,7 @@ export default class Decoder { for (let idx = 0; idx < length; idx++) { const result = Decoder.decodeParam(param.subtype, slices, newOffset); + newOffset = result.newOffset; tokens.push(result.token); } @@ -132,6 +134,7 @@ export default class Decoder { for (let idx = 0; idx < param.length; idx++) { const result = Decoder.decodeParam(param.subtype, slices, newOffset); + newOffset = result.newOffset; tokens.push(result.token); } diff --git a/js/src/abi/decoder/decoder.spec.js b/js/src/abi/decoder/decoder.spec.js index cb8f69173..d06e6b716 100644 --- a/js/src/abi/decoder/decoder.spec.js +++ b/js/src/abi/decoder/decoder.spec.js @@ -100,6 +100,7 @@ describe('abi/decoder/Decoder', () => { it('throws an error on invalid param type', () => { const pt = new ParamType('address'); + pt._type = 'noMatch'; expect(() => Decoder.decodeParam(pt)).to.throw(/noMatch/); diff --git a/js/src/abi/encoder/encoder.spec.js b/js/src/abi/encoder/encoder.spec.js index 27592fb54..e847710a2 100644 --- a/js/src/abi/encoder/encoder.spec.js +++ b/js/src/abi/encoder/encoder.spec.js @@ -93,6 +93,7 @@ describe('abi/encoder/Encoder', () => { it('throws an Error on invalid tokens', () => { const token = new Token('address'); + token._type = 'noMatch'; expect(() => Encoder.encodeToken(token)).to.throw(/noMatch/); diff --git a/js/src/abi/spec/event/event.js b/js/src/abi/spec/event/event.js index 025f2c316..f82bcd4bc 100644 --- a/js/src/abi/spec/event/event.js +++ b/js/src/abi/spec/event/event.js @@ -27,6 +27,7 @@ export default class Event { this._anonymous = !!abi.anonymous; const { id, name, signature } = eventSignature(abi.name, this.inputParamTypes()); + this._id = id; this._name = name; this._signature = signature; diff --git a/js/src/abi/spec/event/eventParam.spec.js b/js/src/abi/spec/event/eventParam.spec.js index e285707f2..1b9497f90 100644 --- a/js/src/abi/spec/event/eventParam.spec.js +++ b/js/src/abi/spec/event/eventParam.spec.js @@ -20,6 +20,7 @@ describe('abi/spec/event/EventParam', () => { describe('constructor', () => { it('sets the properties', () => { const param = new EventParam('foo', 'uint', true); + expect(param.name).to.equal('foo'); expect(param.kind.type).to.equal('uint'); expect(param.indexed).to.be.true; diff --git a/js/src/abi/spec/function.js b/js/src/abi/spec/function.js index 04e059c47..2c0af4143 100644 --- a/js/src/abi/spec/function.js +++ b/js/src/abi/spec/function.js @@ -28,6 +28,7 @@ export default class Func { this._outputs = Param.toParams(abi.outputs || []); const { id, name, signature } = methodSignature(abi.name, this.inputParamTypes()); + this._id = id; this._name = name; this._signature = signature; diff --git a/js/src/abi/util/pad.js b/js/src/abi/util/pad.js index ce8777974..7004a8646 100644 --- a/js/src/abi/util/pad.js +++ b/js/src/abi/util/pad.js @@ -47,6 +47,7 @@ function stringToBytes (input) { return input; } else if (input.substr(0, 2) === '0x') { const matches = input.substr(2).toLowerCase().match(/.{1,2}/g) || []; + return matches.map((value) => parseInt(value, 16)); } else { return input.split('').map((char) => char.charCodeAt(0)); diff --git a/js/src/abi/util/signature.js b/js/src/abi/util/signature.js index ad2b7ca5b..2ae1812a4 100644 --- a/js/src/abi/util/signature.js +++ b/js/src/abi/util/signature.js @@ -40,6 +40,7 @@ function parseName (name) { } const trimmedName = strName.slice(0, idx); + return { strName: trimmedName, name: trimmedName diff --git a/js/src/abi/util/sliceAs.spec.js b/js/src/abi/util/sliceAs.spec.js index 9ad4cf831..db4dd8e41 100644 --- a/js/src/abi/util/sliceAs.spec.js +++ b/js/src/abi/util/sliceAs.spec.js @@ -22,6 +22,7 @@ describe('abi/util/sliceAs', () => { describe('asAddress', () => { it('correctly returns the last 0x40 characters', () => { const address = '1111111111222222222233333333334444444444'; + expect(asAddress(`000000000000000000000000${address}`)).to.equal(`0x${address}`); }); }); diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index df134fbfd..702214a74 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -258,6 +258,7 @@ export default class Contract { if (!func.constant) { func.postTransaction = (options, values = []) => { const _options = this._encodeOptions(func, this._addOptionsTo(options), values); + return this._api.parity .postTransaction(_options) .catch((error) => { @@ -268,6 +269,7 @@ export default class Contract { func.estimateGas = (options, values = []) => { const _options = this._encodeOptions(func, this._addOptionsTo(options), values); + return this._api.eth .estimateGas(_options) .catch((error) => { @@ -303,6 +305,7 @@ export default class Contract { } const options = this._getFilterOptions(event, _options); + options.fromBlock = 0; options.toBlock = 'latest'; @@ -318,6 +321,7 @@ export default class Contract { if (eventName && !event) { const events = this._events.map((evt) => evt.name).join(', '); + throw new Error(`${eventName} is not a valid eventName, subscribe using one of ${events} (or null to include all)`); } @@ -344,12 +348,14 @@ export default class Contract { _createEthFilter (event = null, _options) { const options = this._getFilterOptions(event, _options); + return this._api.eth.newFilter(options); } subscribe (eventName = null, options = {}, callback, autoRemove) { try { const event = this._findEvent(eventName); + return this._subscribe(event, options, callback, autoRemove); } catch (e) { return Promise.reject(e); @@ -374,6 +380,7 @@ export default class Contract { _subscribe (event = null, _options, callback, autoRemove = false) { const subscriptionId = nextSubscriptionId++; const { skipInitFetch } = _options; + delete _options['skipInitFetch']; return this diff --git a/js/src/api/contract/contract.spec.js b/js/src/api/contract/contract.spec.js index 0258aea58..5f408e1da 100644 --- a/js/src/api/contract/contract.spec.js +++ b/js/src/api/contract/contract.spec.js @@ -143,6 +143,7 @@ describe('api/contract/Contract', () => { type: 'event' } ]); + contract.at('6789'); expect(Object.keys(contract.instance)).to.deep.equal([ diff --git a/js/src/api/format/input.spec.js b/js/src/api/format/input.spec.js index 699ac93c2..abe883f0f 100644 --- a/js/src/api/format/input.spec.js +++ b/js/src/api/format/input.spec.js @@ -105,6 +105,7 @@ describe('api/format/input', () => { ['address'].forEach((input) => { it(`formats ${input} address as address`, () => { const block = {}; + block[input] = address; const formatted = inFilter(block)[input]; @@ -116,6 +117,7 @@ describe('api/format/input', () => { ['fromBlock', 'toBlock'].forEach((input) => { it(`formats ${input} number as blockNumber`, () => { const block = {}; + block[input] = 0x123; const formatted = inFilter(block)[input]; @@ -186,6 +188,7 @@ describe('api/format/input', () => { ['data'].forEach((input) => { it(`converts ${input} to hex data`, () => { const block = {}; + block[input] = '1234'; const formatted = inData(block[input]); @@ -196,6 +199,7 @@ describe('api/format/input', () => { ['from', 'to'].forEach((input) => { it(`formats ${input} address as address`, () => { const block = {}; + block[input] = address; const formatted = inOptions(block)[input]; @@ -207,6 +211,7 @@ describe('api/format/input', () => { ['gas', 'gasPrice', 'value', 'minBlock', 'nonce'].forEach((input) => { it(`formats ${input} number as hexnumber`, () => { const block = {}; + block[input] = 0x123; const formatted = inOptions(block)[input]; @@ -250,11 +255,13 @@ describe('api/format/input', () => { describe('inTraceType', () => { it('returns array of types as is', () => { const types = ['vmTrace', 'trace', 'stateDiff']; + expect(inTraceType(types)).to.deep.equal(types); }); it('formats single string type into array', () => { const type = 'vmTrace'; + expect(inTraceType(type)).to.deep.equal([type]); }); }); diff --git a/js/src/api/format/output.spec.js b/js/src/api/format/output.spec.js index cdf96601e..f262a0b6a 100644 --- a/js/src/api/format/output.spec.js +++ b/js/src/api/format/output.spec.js @@ -59,6 +59,7 @@ describe('api/format/output', () => { ['author', 'miner'].forEach((input) => { it(`formats ${input} address as address`, () => { const block = {}; + block[input] = address; const formatted = outBlock(block)[input]; @@ -70,6 +71,7 @@ describe('api/format/output', () => { ['difficulty', 'gasLimit', 'gasUsed', 'number', 'nonce', 'totalDifficulty'].forEach((input) => { it(`formats ${input} number as hexnumber`, () => { const block = {}; + block[input] = 0x123; const formatted = outBlock(block)[input]; @@ -81,6 +83,7 @@ describe('api/format/output', () => { ['timestamp'].forEach((input) => { it(`formats ${input} number as Date`, () => { const block = {}; + block[input] = 0x57513668; const formatted = outBlock(block)[input]; @@ -219,6 +222,7 @@ describe('api/format/output', () => { ['contractAddress'].forEach((input) => { it(`formats ${input} address as address`, () => { const block = {}; + block[input] = address; const formatted = outReceipt(block)[input]; @@ -230,6 +234,7 @@ describe('api/format/output', () => { ['blockNumber', 'cumulativeGasUsed', 'cumulativeGasUsed', 'gasUsed', 'transactionIndex'].forEach((input) => { it(`formats ${input} number as hexnumber`, () => { const block = {}; + block[input] = 0x123; const formatted = outReceipt(block)[input]; @@ -283,6 +288,7 @@ describe('api/format/output', () => { ['from', 'to'].forEach((input) => { it(`formats ${input} address as address`, () => { const block = {}; + block[input] = address; const formatted = outTransaction(block)[input]; @@ -294,6 +300,7 @@ describe('api/format/output', () => { ['blockNumber', 'gasPrice', 'gas', 'minBlock', 'nonce', 'transactionIndex', 'value'].forEach((input) => { it(`formats ${input} number as hexnumber`, () => { const block = {}; + block[input] = 0x123; const formatted = outTransaction(block)[input]; diff --git a/js/src/api/subscriptions/eth.spec.js b/js/src/api/subscriptions/eth.spec.js index 680ff881e..fe8d7b794 100644 --- a/js/src/api/subscriptions/eth.spec.js +++ b/js/src/api/subscriptions/eth.spec.js @@ -34,6 +34,7 @@ function stubApi (blockNumber) { eth: { blockNumber: () => { const stub = sinon.stub().resolves(new BigNumber(blockNumber || START_BLOCK))(); + _calls.blockNumber.push(stub); return stub; } diff --git a/js/src/api/subscriptions/personal.spec.js b/js/src/api/subscriptions/personal.spec.js index 2359192f0..f27f83f70 100644 --- a/js/src/api/subscriptions/personal.spec.js +++ b/js/src/api/subscriptions/personal.spec.js @@ -36,6 +36,7 @@ function stubApi (accounts, info) { parity: { allAccountsInfo: () => { const stub = sinon.stub().resolves(info || TEST_INFO)(); + _calls.allAccountsInfo.push(stub); return stub; } @@ -43,6 +44,7 @@ function stubApi (accounts, info) { eth: { accounts: () => { const stub = sinon.stub().resolves(accounts || TEST_LIST)(); + _calls.listAccounts.push(stub); return stub; } diff --git a/js/src/api/transport/error.js b/js/src/api/transport/error.js index 512d4289e..331852a38 100644 --- a/js/src/api/transport/error.js +++ b/js/src/api/transport/error.js @@ -44,6 +44,7 @@ export const ERROR_CODES = { export default class TransportError extends ExtendableError { constructor (method, code, message) { const m = `${method}: ${code}: ${message}`; + super(m); this.code = code; diff --git a/js/src/api/transport/http/http.js b/js/src/api/transport/http/http.js index 36e0ae1b7..82ab2c4ac 100644 --- a/js/src/api/transport/http/http.js +++ b/js/src/api/transport/http/http.js @@ -75,6 +75,7 @@ export default class Http extends JsonRpcBase { console.error(`${method}(${JSON.stringify(params)}): ${response.error.code}: ${response.error.message}`); const error = new TransportError(method, response.error.code, response.error.message); + throw error; } diff --git a/js/src/api/transport/ws/ws.js b/js/src/api/transport/ws/ws.js index ef739ae13..ca5bde26f 100644 --- a/js/src/api/transport/ws/ws.js +++ b/js/src/api/transport/ws/ws.js @@ -212,6 +212,7 @@ export default class Ws extends JsonRpcBase { } const error = new TransportError(method, result.error.code, result.error.message); + reject(error); delete this._messages[result.id]; diff --git a/js/src/api/util/format.js b/js/src/api/util/format.js index f6976c13b..cfd642cdb 100644 --- a/js/src/api/util/format.js +++ b/js/src/api/util/format.js @@ -48,11 +48,13 @@ export function asciiToHex (string) { export function padRight (input, length) { const value = toHex(input).substr(2, length * 2); + return '0x' + value + range(length * 2 - value.length).map(() => '0').join(''); } export function padLeft (input, length) { const value = toHex(input).substr(2, length * 2); + return '0x' + range(length * 2 - value.length).map(() => '0').join('') + value; } diff --git a/js/src/api/util/sha3.js b/js/src/api/util/sha3.js index f07f88068..e68073103 100644 --- a/js/src/api/util/sha3.js +++ b/js/src/api/util/sha3.js @@ -24,6 +24,7 @@ export function sha3 (value, options) { if (forceHex || (!options && isHex(value))) { const bytes = hexToBytes(value); + return sha3(bytes); } diff --git a/js/src/api/util/sha3.spec.js b/js/src/api/util/sha3.spec.js index 2945ce51a..147b844f3 100644 --- a/js/src/api/util/sha3.spec.js +++ b/js/src/api/util/sha3.spec.js @@ -24,6 +24,7 @@ describe('api/util/sha3', () => { it('constructs a correct sha3 encoded as hex', () => { const key = '000000000000000000000000391694e7e0b0cce554cb130d723a9d27458f9298' + '0000000000000000000000000000000000000000000000000000000000000001'; + expect(sha3(key, { encoding: 'hex' })).to.equal('0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9'); expect(sha3(`0x${key}`, { encoding: 'hex' })).to.equal('0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9'); }); diff --git a/js/src/contracts/badgereg.js b/js/src/contracts/badgereg.js index b9b10c19f..18cb872fc 100644 --- a/js/src/contracts/badgereg.js +++ b/js/src/contracts/badgereg.js @@ -67,6 +67,7 @@ export default class BadgeReg { return this.fetchMeta(id) .then(({ title, icon }) => { const data = { address, id, name, title, icon }; + this.certifiers[id] = data; return data; }); @@ -87,6 +88,7 @@ export default class BadgeReg { return this.fetchMeta(id) .then(({ title, icon }) => { const data = { address, id, name, title, icon }; + this.certifiers[id] = data; return data; }); diff --git a/js/src/contracts/verification.js b/js/src/contracts/verification.js index 3940e0e18..407a61917 100644 --- a/js/src/contracts/verification.js +++ b/js/src/contracts/verification.js @@ -76,6 +76,7 @@ export const awaitPuzzle = (api, contract, account) => { from: block.toNumber(), filter: (log) => log.params.who.value === account }); + subscription.once('error', reject); subscription.once('log', subscription.unsubscribe); subscription.once('log', resolve); diff --git a/js/src/dapps/basiccoin/Deploy/Deployment/deployment.js b/js/src/dapps/basiccoin/Deploy/Deployment/deployment.js index 63f0bbe21..e118bca80 100644 --- a/js/src/dapps/basiccoin/Deploy/Deployment/deployment.js +++ b/js/src/dapps/basiccoin/Deploy/Deployment/deployment.js @@ -293,6 +293,7 @@ export default class Deployment extends Component { this.setState({ deployState: 'Gas estimated, Posting transaction to the network' }); const gasPassed = gas.mul(1.2); + options.gas = gasPassed.toFixed(0); console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`); diff --git a/js/src/dapps/basiccoin/Transfer/Events/events.js b/js/src/dapps/basiccoin/Transfer/Events/events.js index c85898fc9..0febd38a0 100644 --- a/js/src/dapps/basiccoin/Transfer/Events/events.js +++ b/js/src/dapps/basiccoin/Transfer/Events/events.js @@ -37,6 +37,7 @@ export default class Events extends Component { loadAllTokens() .then((tokens) => { const addresses = tokens.map((token) => token.address); + this.setState({ tokens }); return subscribeEvents(addresses, this.eventCallback); }) @@ -144,6 +145,7 @@ export default class Events extends Component { .concat(pendingEvents) .filter((log) => !minedNew.find((event) => event.transactionHash === log.transactionHash)); const events = [].concat(pendingNew).concat(minedNew); + this.setState({ loading: false, events, minedEvents: minedNew, pendingEvents: pendingNew }); } } diff --git a/js/src/dapps/basiccoin/Transfer/Send/send.js b/js/src/dapps/basiccoin/Transfer/Send/send.js index 74ef907a7..204dd0768 100644 --- a/js/src/dapps/basiccoin/Transfer/Send/send.js +++ b/js/src/dapps/basiccoin/Transfer/Send/send.js @@ -275,6 +275,7 @@ export default class Send extends Component { this.setState({ sendState: 'Gas estimated, Posting transaction to the network' }); const gasPassed = gas.mul(1.2); + options.gas = gasPassed.toFixed(0); console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`); diff --git a/js/src/dapps/basiccoin/services.js b/js/src/dapps/basiccoin/services.js index 0344df805..79df78097 100644 --- a/js/src/dapps/basiccoin/services.js +++ b/js/src/dapps/basiccoin/services.js @@ -105,6 +105,7 @@ export function attachInstances () { ]) .then(([registryAddress, netChain]) => { const registry = api.newContract(abis.registry, registryAddress).instance; + isTest = ['morden', 'ropsten', 'testnet'].includes(netChain); console.log(`contract was found at registry=${registryAddress}`); @@ -180,6 +181,7 @@ export function loadOwnedTokens (addresses) { .then((_tokens) => { const tokens = _tokens.reduce((tokens, token) => { const [address, owner, tokenreg] = token; + tokens[owner] = tokens[owner] || []; tokens[owner].push({ address, owner, tokenreg }); return tokens; @@ -212,6 +214,7 @@ export function loadAllTokens () { .all( _tokens.map(([address, owner, tokenreg]) => { const isGlobal = tokenreg === tokenregInstance.address; + tokens.push({ address, owner, tokenreg, isGlobal }); return registries[tokenreg].fromAddress.call({}, [address]); }) @@ -219,6 +222,7 @@ export function loadAllTokens () { .then((coins) => { return tokens.map((token, index) => { const [id, tla, base, name, owner] = coins[index]; + token.coin = { id, tla, base, name, owner }; return token; }); @@ -243,6 +247,7 @@ export function loadBalances (addresses) { .then((_balances) => { return tokens.map((token, tindex) => { const balances = _balances[tindex]; + token.balances = addresses.map((address, aindex) => { return { address, balance: balances[aindex] }; }); diff --git a/js/src/dapps/dappreg/Dapp/dapp.js b/js/src/dapps/dappreg/Dapp/dapp.js index 2a89d6cbd..2b5e9380b 100644 --- a/js/src/dapps/dappreg/Dapp/dapp.js +++ b/js/src/dapps/dappreg/Dapp/dapp.js @@ -100,6 +100,7 @@ export default class Dapp extends Component { const hash = app[`${type}Hash`]; let overlayImage = null; + if (withImage && hash) { overlayImage = ( diff --git a/js/src/dapps/dappreg/dappsStore.js b/js/src/dapps/dappreg/dappsStore.js index 2c6fc689e..27f905da1 100644 --- a/js/src/dapps/dappreg/dappsStore.js +++ b/js/src/dapps/dappreg/dappsStore.js @@ -145,6 +145,7 @@ export default class DappsStore { @action setApps = (apps) => { this.sortApps(apps.filter((app) => { const bnid = new BigNumber(app.id); + return bnid.gt(0); })); @@ -194,6 +195,7 @@ export default class DappsStore { .keys(accountsInfo) .map((address) => { const account = accountsInfo[address]; + account.address = address; return account; }); diff --git a/js/src/dapps/githubhint/Application/application.js b/js/src/dapps/githubhint/Application/application.js index f0599a8d8..008a2326d 100644 --- a/js/src/dapps/githubhint/Application/application.js +++ b/js/src/dapps/githubhint/Application/application.js @@ -78,6 +78,7 @@ export default class Application extends Component { const { fromAddress, registerBusy, url, urlError, contentHash, contentHashError, contentHashOwner, commit, commitError, registerType, repo, repoError } = this.state; let hashClass = null; + if (contentHashError) { hashClass = contentHashOwner !== fromAddress ? styles.hashError : styles.hashWarning; } else if (contentHash) { @@ -85,6 +86,7 @@ export default class Application extends Component { } let valueInputs = null; + if (registerType === 'content') { valueInputs = [
@@ -275,6 +277,7 @@ export default class Application extends Component { // TODO: field validation if (!urlError) { const parts = url.split('/'); + hasContent = parts.length !== 0; if (parts[2] === 'github.com' || parts[2] === 'raw.githubusercontent.com') { @@ -366,6 +369,7 @@ export default class Application extends Component { registerContent (contentRepo, contentCommit) { const { contentHash, fromAddress, instance } = this.state; + contentCommit = contentCommit.substr(0, 2) === '0x' ? contentCommit : `0x${contentCommit}`; const eventId = nextEventId++; @@ -407,6 +411,7 @@ export default class Application extends Component { }); const gasPassed = gas.mul(1.2); + options.gas = gasPassed.toFixed(0); console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`); @@ -456,6 +461,7 @@ export default class Application extends Component { }); const gasPassed = gas.mul(1.2); + options.gas = gasPassed.toFixed(0); console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`); diff --git a/js/src/dapps/localtx/Application/application.js b/js/src/dapps/localtx/Application/application.js index 0e2642657..a97fcd3a8 100644 --- a/js/src/dapps/localtx/Application/application.js +++ b/js/src/dapps/localtx/Application/application.js @@ -66,6 +66,7 @@ export default class Application extends Component { .filter(tx => tx.isLocal) .map(data => { const tx = data.transaction; + local[tx.hash].transaction = tx; local[tx.hash].stats = data.stats; }); @@ -73,6 +74,7 @@ export default class Application extends Component { // Convert local transactions to array const localTransactions = Object.keys(local).map(hash => { const data = local[hash]; + data.txHash = hash; return data; }); @@ -124,6 +126,7 @@ export default class Application extends Component { renderQueueSummary () { const { transactions } = this.state; + if (!transactions.length) { return null; } @@ -146,6 +149,7 @@ export default class Application extends Component { renderQueue () { const { blockNumber, transactions } = this.state; + if (!transactions.length) { return (

The queue seems is empty.

@@ -177,6 +181,7 @@ export default class Application extends Component { renderLocals () { const { localTransactions } = this.state; + if (!localTransactions.length) { return (

You haven't sent any transactions yet.

diff --git a/js/src/dapps/localtx/Transaction/transaction.js b/js/src/dapps/localtx/Transaction/transaction.js index 8eecaad45..0dcac1f23 100644 --- a/js/src/dapps/localtx/Transaction/transaction.js +++ b/js/src/dapps/localtx/Transaction/transaction.js @@ -168,11 +168,13 @@ export class Transaction extends BaseTransaction { renderTime (firstSeen) { const { blockNumber } = this.props; + if (!firstSeen) { return 'never'; } const timeInMinutes = blockNumber.sub(firstSeen).mul(14).div(60).toFormat(1); + return `${timeInMinutes} minutes ago`; } } diff --git a/js/src/dapps/registry/Container.js b/js/src/dapps/registry/Container.js index 3d5cf1dfe..a84bfd3a9 100644 --- a/js/src/dapps/registry/Container.js +++ b/js/src/dapps/registry/Container.js @@ -56,6 +56,7 @@ export default connect( // react -> redux connection (dispatch) => { const bound = bindActionCreators(actions, dispatch); + bound.addresses = bindActionCreators(actions.addresses, dispatch); bound.accounts = bindActionCreators(actions.accounts, dispatch); bound.lookup = bindActionCreators(actions.lookup, dispatch); diff --git a/js/src/dapps/registry/Events/actions.js b/js/src/dapps/registry/Events/actions.js index 6303b1bc5..6fb41ac6e 100644 --- a/js/src/dapps/registry/Events/actions.js +++ b/js/src/dapps/registry/Events/actions.js @@ -59,6 +59,7 @@ export const subscribe = (name, from = 0, to = 'pending') => parameters: e.params, timestamp: block.timestamp }; + dispatch(event(name, data)); }) .catch((err) => { diff --git a/js/src/dapps/registry/Events/events.js b/js/src/dapps/registry/Events/events.js index 58cfc120f..0c79922de 100644 --- a/js/src/dapps/registry/Events/events.js +++ b/js/src/dapps/registry/Events/events.js @@ -72,6 +72,7 @@ const renderEvent = (classNames, verb) => (e) => { const renderDataChanged = (e) => { let classNames = styles.dataChanged; + if (e.state === 'pending') { classNames += ' ' + styles.pending; } @@ -113,6 +114,7 @@ const renderReverse = (e) => { } const classes = [ styles.reverse ]; + if (e.state === 'pending') { classes.push(styles.pending); } diff --git a/js/src/dapps/registry/Lookup/actions.js b/js/src/dapps/registry/Lookup/actions.js index 3a8ef515e..343e6ca45 100644 --- a/js/src/dapps/registry/Lookup/actions.js +++ b/js/src/dapps/registry/Lookup/actions.js @@ -29,6 +29,7 @@ export const fail = (action) => ({ type: `${action} error` }); export const lookup = (name, key) => (dispatch, getState) => { const { contract } = getState(); + if (!contract) { return; } diff --git a/js/src/dapps/registry/Names/names.js b/js/src/dapps/registry/Names/names.js index f9ce24cd7..bd983a97d 100644 --- a/js/src/dapps/registry/Names/names.js +++ b/js/src/dapps/registry/Names/names.js @@ -55,6 +55,7 @@ const renderQueue = (queue) => { const grouped = queue.reduce((grouped, change) => { const last = grouped[grouped.length - 1]; + if (last && last.action === change.action) { last.names.push(change.name); } else { diff --git a/js/src/dapps/registry/Records/records.js b/js/src/dapps/registry/Records/records.js index 73e513342..6c786bb44 100644 --- a/js/src/dapps/registry/Records/records.js +++ b/js/src/dapps/registry/Records/records.js @@ -120,6 +120,7 @@ class Records extends Component { onSaveClick = () => { const { name, type, value } = this.state; + this.props.update(name, type, value); }; diff --git a/js/src/dapps/registry/Reverse/reverse.js b/js/src/dapps/registry/Reverse/reverse.js index 0216d00a2..8929c0d9e 100644 --- a/js/src/dapps/registry/Reverse/reverse.js +++ b/js/src/dapps/registry/Reverse/reverse.js @@ -59,6 +59,7 @@ class Reverse extends Component { ); let addressInput = null; + if (action === 'propose') { addressInput = ( (dispatch) => api.parity.registryAddress() .then((address) => { const contract = api.newContract(registryAbi, address); + dispatch(setContract(contract)); dispatch(fetchFee()); dispatch(fetchOwner()); @@ -65,6 +66,7 @@ export const setFee = (fee) => ({ type: 'set fee', fee }); const fetchFee = () => (dispatch, getState) => { const { contract } = getState(); + if (!contract) { return; } @@ -83,6 +85,7 @@ export const setOwner = (owner) => ({ type: 'set owner', owner }); export const fetchOwner = () => (dispatch, getState) => { const { contract } = getState(); + if (!contract) { return; } diff --git a/js/src/dapps/registry/addresses/accounts-reducer.js b/js/src/dapps/registry/addresses/accounts-reducer.js index 926536be0..46a2919e7 100644 --- a/js/src/dapps/registry/addresses/accounts-reducer.js +++ b/js/src/dapps/registry/addresses/accounts-reducer.js @@ -27,6 +27,7 @@ export default (state = initialState, action) => { accounts[account.address] = account; return accounts; }, {}); + return { ...state, all: accounts }; } diff --git a/js/src/dapps/registry/addresses/actions.js b/js/src/dapps/registry/addresses/actions.js index 9f4de0817..d9fc002d1 100644 --- a/js/src/dapps/registry/addresses/actions.js +++ b/js/src/dapps/registry/addresses/actions.js @@ -29,6 +29,7 @@ export const fetch = () => (dispatch) => { address, isAccount: true })); + dispatch(set(addresses)); }) .catch((error) => { diff --git a/js/src/dapps/registry/addresses/contacts-reducer.js b/js/src/dapps/registry/addresses/contacts-reducer.js index 401302520..842ac160f 100644 --- a/js/src/dapps/registry/addresses/contacts-reducer.js +++ b/js/src/dapps/registry/addresses/contacts-reducer.js @@ -24,6 +24,7 @@ export default (state = initialState, action) => { contacts[contact.address] = contact; return contacts; }, {}); + return contacts; } diff --git a/js/src/dapps/registry/ui/hash.js b/js/src/dapps/registry/ui/hash.js index 6eeaab7b2..48c78f1d9 100644 --- a/js/src/dapps/registry/ui/hash.js +++ b/js/src/dapps/registry/ui/hash.js @@ -38,6 +38,7 @@ class Hash extends Component { const { hash, isTestnet, linked } = this.props; let shortened = hash.toLowerCase().replace(leading0x, ''); + shortened = shortened.length > (6 + 6) ? shortened.substr(0, 6) + '...' + shortened.slice(-6) : shortened; diff --git a/js/src/dapps/tokenreg/Accounts/AccountSelector/account-selector.js b/js/src/dapps/tokenreg/Accounts/AccountSelector/account-selector.js index 6c71578a0..978936ab8 100644 --- a/js/src/dapps/tokenreg/Accounts/AccountSelector/account-selector.js +++ b/js/src/dapps/tokenreg/Accounts/AccountSelector/account-selector.js @@ -33,6 +33,7 @@ class AccountSelectorItem extends Component { const account = this.props.account; const props = Object.assign({}, this.props); + delete props.account; delete props.onSelectAccount; diff --git a/js/src/dapps/tokenreg/Accounts/AccountSelector/container.js b/js/src/dapps/tokenreg/Accounts/AccountSelector/container.js index 9711c05bf..60962cddd 100644 --- a/js/src/dapps/tokenreg/Accounts/AccountSelector/container.js +++ b/js/src/dapps/tokenreg/Accounts/AccountSelector/container.js @@ -33,6 +33,7 @@ class AccountSelectorContainer extends Component { const mapStateToProps = (state) => { const { accounts } = state; + return { ...accounts }; }; diff --git a/js/src/dapps/tokenreg/Actions/Register/register.js b/js/src/dapps/tokenreg/Actions/Register/register.js index 2c084cec2..33913497c 100644 --- a/js/src/dapps/tokenreg/Actions/Register/register.js +++ b/js/src/dapps/tokenreg/Actions/Register/register.js @@ -188,6 +188,7 @@ export default class RegisterAction extends Component { onAccountChange = () => { const { dialog } = this.refs; + dialog.forceUpdate(); } diff --git a/js/src/dapps/tokenreg/Inputs/Text/input-text.js b/js/src/dapps/tokenreg/Inputs/Text/input-text.js index 2184b6b50..c5f92bfb1 100644 --- a/js/src/dapps/tokenreg/Inputs/Text/input-text.js +++ b/js/src/dapps/tokenreg/Inputs/Text/input-text.js @@ -98,11 +98,11 @@ export default class InputText extends Component { onChange = (event) => { const value = event.target.value; + // So we can focus on the input after async validation event.persist(); const { validationType, contract } = this.props; - const validation = validate(value, validationType, contract); if (validation instanceof Promise) { diff --git a/js/src/dapps/tokenreg/Tokens/Token/token.js b/js/src/dapps/tokenreg/Tokens/Token/token.js index 1500d194e..926e3e9f4 100644 --- a/js/src/dapps/tokenreg/Tokens/Token/token.js +++ b/js/src/dapps/tokenreg/Tokens/Token/token.js @@ -381,6 +381,7 @@ export default class Token extends Component { onUnregister = () => { const index = this.props.index; + this.props.handleUnregister(index); } diff --git a/js/src/dapps/tokenreg/Tokens/actions.js b/js/src/dapps/tokenreg/Tokens/actions.js index 8ec37fd17..99dd5d440 100644 --- a/js/src/dapps/tokenreg/Tokens/actions.js +++ b/js/src/dapps/tokenreg/Tokens/actions.js @@ -77,6 +77,7 @@ export const loadTokens = () => (dispatch, getState) => { .call() .then((count) => { const tokenCount = parseInt(count); + dispatch(setTokenCount(tokenCount)); for (let i = 0; i < tokenCount; i++) { @@ -93,7 +94,6 @@ export const loadTokens = () => (dispatch, getState) => { export const loadToken = (index) => (dispatch, getState) => { const state = getState(); const contractInstance = state.status.contract.instance; - const userAccounts = state.accounts.list; const accountsInfo = state.accounts.accountsInfo; @@ -104,11 +104,9 @@ export const loadToken = (index) => (dispatch, getState) => { .call({}, [ parseInt(index) ]) .then((result) => { const tokenOwner = result[4]; - const isTokenOwner = userAccounts .filter(a => a.address === tokenOwner) .length > 0; - const data = { index: parseInt(index), address: result[0], @@ -156,8 +154,8 @@ export const loadToken = (index) => (dispatch, getState) => { export const queryTokenMeta = (index, query) => (dispatch, getState) => { const state = getState(); const contractInstance = state.status.contract.instance; - const startDate = Date.now(); + dispatch(setTokenMetaLoading(index, true)); contractInstance @@ -184,7 +182,6 @@ export const addTokenMeta = (index, key, value) => (dispatch, getState) => { const state = getState(); const contractInstance = state.status.contract.instance; const token = state.tokens.tokens.find(t => t.index === index); - const options = { from: token.owner }; const values = [ index, key, value ]; @@ -203,9 +200,7 @@ export const addTokenMeta = (index, key, value) => (dispatch, getState) => { export const addGithubhintURL = (from, key, url) => (dispatch, getState) => { const state = getState(); const contractInstance = state.status.githubhint.instance; - const options = { from }; - const values = [ key, url ]; contractInstance @@ -223,7 +218,6 @@ export const addGithubhintURL = (from, key, url) => (dispatch, getState) => { export const unregisterToken = (index) => (dispatch, getState) => { const { contract } = getState().status; const { instance, owner } = contract; - const values = [ index ]; const options = { from: owner diff --git a/js/src/environment/integration-tests/fake-backend.js b/js/src/environment/integration-tests/fake-backend.js index 3c71edaaf..e87adad90 100644 --- a/js/src/environment/integration-tests/fake-backend.js +++ b/js/src/environment/integration-tests/fake-backend.js @@ -77,6 +77,7 @@ class FakeRpcServer { } const fakeRpc = new FakeRpcServer(); + fakeRpc.start(); mockedResponses.rpc.forEach(method => fakeRpc.simpleRpc(method.name, method.response)); diff --git a/js/src/environment/tests/test-utils.js b/js/src/environment/tests/test-utils.js index acf7ad7ab..1cd1e37f8 100644 --- a/js/src/environment/tests/test-utils.js +++ b/js/src/environment/tests/test-utils.js @@ -20,6 +20,7 @@ const isProd = process.env.NODE_ENV === 'production'; // Component utils for integration tests hooks. const TEST_HOOK = 'data-test'; + Component.prototype._test = isProd ? noop : testHook; Component.prototype._testInherit = isProd ? noop : testHookInherit; @@ -27,6 +28,7 @@ function noop (name) {} function testHookInherit (name) { let hook = this.props[TEST_HOOK]; + if (name) { hook += `-${name}`; } @@ -37,6 +39,7 @@ function testHookInherit (name) { function testHook (name) { let hook = this.constructor.name; + if (name) { hook += `-${name}`; } diff --git a/js/src/index.js b/js/src/index.js index 72f26e12f..f56620737 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -49,6 +49,7 @@ injectTapEventPlugin(); if (process.env.NODE_ENV === 'development') { // Expose the React Performance Tools on the`window` object const Perf = require('react-addons-perf'); + window.Perf = Perf; } @@ -56,15 +57,18 @@ const AUTH_HASH = '#/auth?'; const parityUrl = process.env.PARITY_URL || window.location.host; let token = null; + if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) { token = qs.parse(window.location.hash.substr(AUTH_HASH.length)).token; } const api = new SecureApi(`ws://${parityUrl}`, token); + patchApi(api); ContractInstances.create(api); const store = initStore(api, hashHistory); + store.dispatch({ type: 'initAll', api }); store.dispatch(setApi(api)); diff --git a/js/src/modals/CreateAccount/AccountDetailsGeth/accountDetailsGeth.js b/js/src/modals/CreateAccount/AccountDetailsGeth/accountDetailsGeth.js index 4d9cde568..49156c126 100644 --- a/js/src/modals/CreateAccount/AccountDetailsGeth/accountDetailsGeth.js +++ b/js/src/modals/CreateAccount/AccountDetailsGeth/accountDetailsGeth.js @@ -28,6 +28,7 @@ export default class AccountDetailsGeth extends Component { const formatted = addresses.map((address, idx) => { const comma = !idx ? '' : ((idx === addresses.length - 1) ? ' & ' : ', '); + return `${comma}${address}`; }).join(''); diff --git a/js/src/modals/CreateAccount/NewGeth/newGeth.js b/js/src/modals/CreateAccount/NewGeth/newGeth.js index 96e48fa25..8566f76b4 100644 --- a/js/src/modals/CreateAccount/NewGeth/newGeth.js +++ b/js/src/modals/CreateAccount/NewGeth/newGeth.js @@ -91,6 +91,7 @@ export default class NewGeth extends Component { const { available } = this.state; const account = available.find((_account) => _account.address === address); + account.checked = checked; const selected = available.filter((_account) => _account.checked); diff --git a/js/src/modals/CreateAccount/NewImport/newImport.js b/js/src/modals/CreateAccount/NewImport/newImport.js index d1dfdc5d6..6e064770f 100644 --- a/js/src/modals/CreateAccount/NewImport/newImport.js +++ b/js/src/modals/CreateAccount/NewImport/newImport.js @@ -112,6 +112,7 @@ export default class NewImport extends Component { if (el.files.length) { const reader = new FileReader(); + reader.onload = (event) => { this.setState({ walletJson: event.target.result, diff --git a/js/src/modals/CreateAccount/print.js b/js/src/modals/CreateAccount/print.js index 1f92744d4..75e11b933 100644 --- a/js/src/modals/CreateAccount/print.js +++ b/js/src/modals/CreateAccount/print.js @@ -15,13 +15,17 @@ // along with Parity. If not, see . export const onPrint = (window, cb) => { - let called = false; let query, queryFn; + let called = false; + let query; + let queryFn; const onPrint = () => { if (queryFn) { query.removeListener(queryFn); } + window.removeEventListener('afterprint', onPrint, false); + if (!called) { called = true; cb(); @@ -34,14 +38,17 @@ export const onPrint = (window, cb) => { onPrint(); } }; + query = window.matchMedia('print'); query.addListener(queryFn); } + window.addEventListener('afterprint', onPrint, false); }; export default (html) => { const iframe = document.createElement('iframe'); + iframe.setAttribute('sandbox', 'allow-modals allow-same-origin allow-scripts'); iframe.setAttribute('src', '/'); iframe.setAttribute('style', 'display: none'); diff --git a/js/src/modals/CreateWallet/createWalletStore.js b/js/src/modals/CreateWallet/createWalletStore.js index 2a3f08a6b..bac9c12a2 100644 --- a/js/src/modals/CreateWallet/createWalletStore.js +++ b/js/src/modals/CreateWallet/createWalletStore.js @@ -120,11 +120,13 @@ export default class CreateWalletStore { @action onNext = () => { const stepIndex = this.stepsKeys.findIndex((k) => k === this.step) + 1; + this.step = this.stepsKeys[stepIndex]; } @action onChange = (_wallet) => { const newWallet = Object.assign({}, this.wallet, _wallet); + this.validateWallet(newWallet); } diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 041f71527..33002fc22 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -307,6 +307,7 @@ export default class DetailsStep extends Component { inputs.forEach((input) => { const param = parseAbiType(input.type); + params.push(param.default); }); diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index d3db3937f..d48c537f4 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -358,6 +358,7 @@ class DeployContract extends Component { const body = txhash ? : null; + return ( { const values = (func.abi.inputs || []).map((input) => { const parsedType = parseAbiType(input.type); + return parsedType.default; }); diff --git a/js/src/modals/ExecuteContract/executeContract.test.js b/js/src/modals/ExecuteContract/executeContract.test.js index ac1138a17..9006757f6 100644 --- a/js/src/modals/ExecuteContract/executeContract.test.js +++ b/js/src/modals/ExecuteContract/executeContract.test.js @@ -69,6 +69,7 @@ const STORE = { function createApi (result = true) { const sha3 = sinon.stub().resolves('0x0000000000000000000000000000000000000000'); + sha3.text = sha3; return { parity: { diff --git a/js/src/modals/FirstRun/firstRun.js b/js/src/modals/FirstRun/firstRun.js index 12902af4e..1e2c522ce 100644 --- a/js/src/modals/FirstRun/firstRun.js +++ b/js/src/modals/FirstRun/firstRun.js @@ -148,6 +148,7 @@ class FirstRun extends Component { onClick={ this.onCreate } /> ]; + if (hasAccounts) { buttons.unshift(