diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f8b8ad0b..3548835b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ - Update gasLimit params with every new block seen. +## 3.7.7 2017-6-8 + +- Fix bug where metamask would show old data after computer being asleep or disconnected from the internet. + +## 3.7.6 2017-6-5 + +- Fix bug that prevented publishing contracts. + +## 3.7.5 2017-6-5 + +- Prevent users from sending to the `0x0` address. +- Provide useful errors when entering bad characters in ENS name. +- Add ability to copy addresses from transaction confirmation view. + ## 3.7.4 2017-6-2 - Fix bug with inflight cache that caused some block lookups to return bad values (affected OasisDex). diff --git a/README.md b/README.md index 821e1cdfd..afeb96ae5 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,15 @@ If you're a web dapp developer, we've got two types of guides for you: Uncompressed builds can be found in `/dist`, compressed builds can be found in `/builds` once they're built. -## Installing Local Builds on Chrome +### Running Tests + +Requires `mocha` installed. Run `npm install -g mocha`. + +Then just run `npm test`. -To install your locally built extension on Chrome, [follow this guide](http://stackoverflow.com/a/24577660/272576). +You can also test with a continuously watching process, via `npm run watch`. -The built extension is stored in `./dist/chrome/`. +You can run the linter by itself with `gulp lint`. ## Architecture @@ -41,155 +45,22 @@ npm start npm run dist ``` -#### In Chrome - -Open `Settings` > `Extensions`. - -Check "Developer mode". - -At the top, click `Load Unpacked Extension`. - -Navigate to your `metamask-plugin/dist/chrome` folder. - -Click `Select`. - -You now have the plugin, and can click 'inspect views: background plugin' to view its dev console. - -#### In Firefox - -Go to the url `about:debugging`. - -Click the button `Load Temporary Add-On`. - -Select the file `dist/firefox/manifest.json`. - -You can optionally enable debugging, and click `Debug`, for a console window that logs all of Metamask's processes to a single console. - -If you have problems debugging, try connecting to the IRC channel `#webextensions` on `irc.mozilla.org`. - -For longer questions, use the StackOverfow tag `firefox-addons`. - -### Developing on UI Only - -You can run `npm run ui`, and your browser should open a live-reloading demo version of the plugin UI. - -Some actions will crash the app, so this is only for tuning aesthetics, but it allows live-reloading styles, which is a much faster feedback loop than reloading the full extension. - -### Developing on UI with Mocked Background Process - -You can run `npm run mock` and your browser should open a live-reloading demo version of the plugin UI, just like the `npm run ui`, except that it tries to actually perform all normal operations. - -It does not yet connect to a real blockchain (this could be a good test feature later, connecting to a test blockchain), so only local operations work. - -You can reset the mock ui at any time with the `Reset` button at the top of the screen. - -### Developing on Dependencies - -To enjoy the live-reloading that `gulp dev` offers while working on the `web3-provider-engine` or other dependencies: - - 1. Clone the dependency locally. - 2. `npm install` in its folder. - 3. Run `npm link` in its folder. - 4. Run `npm link $DEP_NAME` in this project folder. - 5. Next time you `npm start` it will watch the dependency for changes as well! - -### Running Tests - -Requires `mocha` installed. Run `npm install -g mocha`. - -Then just run `npm test`. - -You can also test with a continuously watching process, via `npm run watch`. - -You can run the linter by itself with `gulp lint`. - #### Writing Browser Tests To write tests that will be run in the browser using QUnit, add your test files to `test/integration/lib`. -### Deploying the UI +## Other Docs - You must be authorized already on the MetaMask plugin. - - 0. Update the version in `app/manifest.json` and the Changelog in `CHANGELOG.md`. - 1. Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=2). - 2. Run `gulp dist` (or `gulp zip` if you've already built) - 3. Upload the latest zip file from `builds/metamask-$PLATFORM-$VERSION.zip` as the updated package. +- [How to add custom build to Chrome](./docs/add-to-chrome.md) +- [How to add custom build to Firefox](./docs/add-to-firefox.md) +- [How to develop a live-reloading UI](./docs/ui-dev-mode.md) +- [Publishing Guide](./docs/publishing.md) +- [How to develop an in-browser mocked UI](./docs/ui-mock-mode.md) +- [How to live reload on local dependency changes](./docs/developing-on-deps.md) +- [How to add new networks to the Provider Menu](./docs/adding-new-networks.md) +- [How to manage notices that appear when the app starts up](./docs/notices.md) +- [How to generate a visualization of this repository's development](./docs/development-visualization.md) [1]: http://www.nomnoml.com/#view/%5B%3Cactor%3Euser%5D%0A%0A%5Bmetamask-ui%7C%0A%20%20%20%5Btools%7C%0A%20%20%20%20%20react%0A%20%20%20%20%20redux%0A%20%20%20%20%20thunk%0A%20%20%20%20%20ethUtils%0A%20%20%20%20%20jazzicon%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20account-detail%0A%20%20%20%20%20accounts%0A%20%20%20%20%20locked-screen%0A%20%20%20%20%20restore-vault%0A%20%20%20%20%20identicon%0A%20%20%20%20%20config%0A%20%20%20%20%20info%0A%20%20%20%5D%0A%20%20%20%5Breducers%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20metamask%0A%20%20%20%20%20identities%0A%20%20%20%5D%0A%20%20%20%5Bactions%7C%0A%20%20%20%20%20%5BaccountManager%5D%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%5D%3A-%3E%5Bactions%5D%0A%20%20%20%5Bactions%5D%3A-%3E%5Breducers%5D%0A%20%20%20%5Breducers%5D%3A-%3E%5Bcomponents%5D%0A%5D%0A%0A%5Bweb%20dapp%7C%0A%20%20%5Bui%20code%5D%0A%20%20%5Bweb3%5D%0A%20%20%5Bmetamask-inpage%5D%0A%20%20%0A%20%20%5B%3Cactor%3Eui%20developer%5D%0A%20%20%5Bui%20developer%5D-%3E%5Bui%20code%5D%0A%20%20%5Bui%20code%5D%3C-%3E%5Bweb3%5D%0A%20%20%5Bweb3%5D%3C-%3E%5Bmetamask-inpage%5D%0A%5D%0A%0A%5Bmetamask-background%7C%0A%20%20%5Bprovider-engine%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bid%20store%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%3E%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%3C-%3E%5Bid%20store%5D%0A%20%20%5Bconfig%20manager%7C%0A%20%20%20%20%5Brpc%20configuration%5D%0A%20%20%20%20%5Bencrypted%20keys%5D%0A%20%20%20%20%5Bwallet%20nicknames%5D%0A%20%20%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%5Bconfig%20manager%5D%0A%20%20%5Bid%20store%5D%3C-%3E%5Bconfig%20manager%5D%0A%5D%0A%0A%5Buser%5D%3C-%3E%5Bmetamask-ui%5D%0A%0A%5Buser%5D%3C%3A--%3A%3E%5Bweb%20dapp%5D%0A%0A%5Bmetamask-contentscript%7C%0A%20%20%5Bplugin%20restart%20detector%5D%0A%20%20%5Brpc%20passthrough%5D%0A%5D%0A%0A%5Brpc%20%7C%0A%20%20%5Bethereum%20blockchain%20%7C%0A%20%20%20%20%5Bcontracts%5D%0A%20%20%20%20%5Baccounts%5D%0A%20%20%5D%0A%5D%0A%0A%5Bweb%20dapp%5D%3C%3A--%3A%3E%5Bmetamask-contentscript%5D%0A%5Bmetamask-contentscript%5D%3C-%3E%5Bmetamask-background%5D%0A%5Bmetamask-background%5D%3C-%3E%5Bmetamask-ui%5D%0A%5Bmetamask-background%5D%3C-%3E%5Brpc%5D%0A -### Generate Development Visualization - -This will generate a video of the repo commit history. - -Install preqs: -``` -brew install gource -brew install ffmpeg -``` - -From the repo dir, pipe `gource` into `ffmpeg`: -``` -gource \ - --seconds-per-day .1 \ - --user-scale 1.5 \ - --default-user-image "./images/icon-512.png" \ - --viewport 1280x720 \ - --auto-skip-seconds .1 \ - --multi-sampling \ - --stop-at-end \ - --highlight-users \ - --hide mouse,progress \ - --file-idle-time 0 \ - --max-files 0 \ - --background-colour 000000 \ - --font-size 18 \ - --date-format "%b %d, %Y" \ - --highlight-dirs \ - --user-friction 0.1 \ - --title "MetaMask Development History" \ - --output-ppm-stream - \ - --output-framerate 30 \ - | ffmpeg -y -r 30 -f image2pipe -vcodec ppm -i - -b 65536K metamask-dev-history.mp4 -``` - -## Generating Notices - -To add a notice: -``` -npm run generateNotice -``` -Enter the body of your notice into the text editor that pops up, without including the body. Be sure to save the file before closing the window! -Afterwards, enter the title of the notice in the command line and press enter. Afterwards, add and commit the new changes made. - -To delete a notice: -``` -npm run deleteNotice -``` -A list of active notices will pop up. Enter the corresponding id in the command line prompt and add and commit the new changes afterwards. - -## Adding Custom Networks - -To add another network to our dropdown menu, make sure the following files are adjusted properly: - -``` -app/scripts/config.js -app/scripts/lib/buy-eth-url.js -app/scripts/lib/config-manager.js -ui/app/app.js -ui/app/components/buy-button-subview.js -ui/app/components/drop-menu-item.js -ui/app/components/network.js -ui/app/components/transaction-list-item.js -ui/app/config.js -ui/app/css/lib.css -ui/lib/account-link.js -ui/lib/explorer-link.js -``` - -You will need: -+ The network ID -+ An RPC Endpoint url -+ An explorer link -+ CSS for the display icon diff --git a/app/manifest.json b/app/manifest.json index 99a083f0f..a0d1500c2 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.7.4", + "version": "3.7.7", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", diff --git a/docs/add-to-chrome.md b/docs/add-to-chrome.md new file mode 100644 index 000000000..ea5213182 --- /dev/null +++ b/docs/add-to-chrome.md @@ -0,0 +1,14 @@ +## Add Custom Build to Chrome + +Open `Settings` > `Extensions`. + +Check "Developer mode". + +At the top, click `Load Unpacked Extension`. + +Navigate to your `metamask-plugin/dist/chrome` folder. + +Click `Select`. + +You now have the plugin, and can click 'inspect views: background plugin' to view its dev console. + diff --git a/docs/add-to-firef.md b/docs/add-to-firef.md new file mode 100644 index 000000000..593d06170 --- /dev/null +++ b/docs/add-to-firef.md @@ -0,0 +1,14 @@ +# Add Custom Build to Firefox + +Go to the url `about:debugging`. + +Click the button `Load Temporary Add-On`. + +Select the file `dist/firefox/manifest.json`. + +You can optionally enable debugging, and click `Debug`, for a console window that logs all of Metamask's processes to a single console. + +If you have problems debugging, try connecting to the IRC channel `#webextensions` on `irc.mozilla.org`. + +For longer questions, use the StackOverfow tag `firefox-addons`. + diff --git a/docs/adding-new-networks.md b/docs/adding-new-networks.md new file mode 100644 index 000000000..ea1453c21 --- /dev/null +++ b/docs/adding-new-networks.md @@ -0,0 +1,25 @@ +## Adding Custom Networks + +To add another network to our dropdown menu, make sure the following files are adjusted properly: + +``` +app/scripts/config.js +app/scripts/lib/buy-eth-url.js +app/scripts/lib/config-manager.js +ui/app/app.js +ui/app/components/buy-button-subview.js +ui/app/components/drop-menu-item.js +ui/app/components/network.js +ui/app/components/transaction-list-item.js +ui/app/config.js +ui/app/css/lib.css +ui/lib/account-link.js +ui/lib/explorer-link.js +``` + +You will need: ++ The network ID ++ An RPC Endpoint url ++ An explorer link ++ CSS for the display icon + diff --git a/docs/developing-on-deps.md b/docs/developing-on-deps.md new file mode 100644 index 000000000..7de3f67a8 --- /dev/null +++ b/docs/developing-on-deps.md @@ -0,0 +1,10 @@ +### Developing on Dependencies + +To enjoy the live-reloading that `gulp dev` offers while working on the `web3-provider-engine` or other dependencies: + + 1. Clone the dependency locally. + 2. `npm install` in its folder. + 3. Run `npm link` in its folder. + 4. Run `npm link $DEP_NAME` in this project folder. + 5. Next time you `npm start` it will watch the dependency for changes as well! + diff --git a/docs/development-visualization.md b/docs/development-visualization.md new file mode 100644 index 000000000..95847300d --- /dev/null +++ b/docs/development-visualization.md @@ -0,0 +1,35 @@ +### Generate Development Visualization + +This will generate a video of the repo commit history. + +Install preqs: +``` +brew install gource +brew install ffmpeg +``` + +From the repo dir, pipe `gource` into `ffmpeg`: +``` +gource \ + --seconds-per-day .1 \ + --user-scale 1.5 \ + --default-user-image "./images/icon-512.png" \ + --viewport 1280x720 \ + --auto-skip-seconds .1 \ + --multi-sampling \ + --stop-at-end \ + --highlight-users \ + --hide mouse,progress \ + --file-idle-time 0 \ + --max-files 0 \ + --background-colour 000000 \ + --font-size 18 \ + --date-format "%b %d, %Y" \ + --highlight-dirs \ + --user-friction 0.1 \ + --title "MetaMask Development History" \ + --output-ppm-stream - \ + --output-framerate 30 \ + | ffmpeg -y -r 30 -f image2pipe -vcodec ppm -i - -b 65536K metamask-dev-history.mp4 +``` + diff --git a/docs/notices.md b/docs/notices.md new file mode 100644 index 000000000..826e6e84e --- /dev/null +++ b/docs/notices.md @@ -0,0 +1,15 @@ +## Generating Notices + +To add a notice: +``` +npm run generateNotice +``` +Enter the body of your notice into the text editor that pops up, without including the body. Be sure to save the file before closing the window! +Afterwards, enter the title of the notice in the command line and press enter. Afterwards, add and commit the new changes made. + +To delete a notice: +``` +npm run deleteNotice +``` +A list of active notices will pop up. Enter the corresponding id in the command line prompt and add and commit the new changes afterwards. + diff --git a/docs/publishing.md b/docs/publishing.md new file mode 100644 index 000000000..00369acf9 --- /dev/null +++ b/docs/publishing.md @@ -0,0 +1,19 @@ +# Publishing Guide + +When publishing a new version of MetaMask, we follow this procedure: + +## Incrementing Version & Changelog + + You must be authorized already on the MetaMask plugin. + +1. Update the version in `app/manifest.json` and the Changelog in `CHANGELOG.md`. +2. Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=2). + +## Publishing + +1. `npm run dist` to generate the latest build. +2. Publish to chrome store. +3. Publish to firefox addon marketplace. +4. Post on Github releases page. +5. `npm run announce`, post that announcement in our public places. + diff --git a/docs/ui-dev-mode.md b/docs/ui-dev-mode.md new file mode 100644 index 000000000..df49d8b04 --- /dev/null +++ b/docs/ui-dev-mode.md @@ -0,0 +1,6 @@ +# Running UI Dev Mode + +You can run `npm run ui`, and your browser should open a live-reloading demo version of the plugin UI. + +Some actions will crash the app, so this is only for tuning aesthetics, but it allows live-reloading styles, which is a much faster feedback loop than reloading the full extension. + diff --git a/docs/ui-mock-mode.md b/docs/ui-mock-mode.md new file mode 100644 index 000000000..bb54dc471 --- /dev/null +++ b/docs/ui-mock-mode.md @@ -0,0 +1,8 @@ +### Developing on UI with Mocked Background Process + +You can run `npm run mock` and your browser should open a live-reloading demo version of the plugin UI, just like the `npm run ui`, except that it tries to actually perform all normal operations. + +It does not yet connect to a real blockchain (this could be a good test feature later, connecting to a test blockchain), so only local operations work. + +You can reset the mock ui at any time with the `Reset` button at the top of the screen. + diff --git a/package.json b/package.json index 9efba3866..c6c3add8e 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "0.18.2", - "web3-provider-engine": "^12.1.0", + "web3-provider-engine": "^12.2.3", "web3-stream-provider": "^2.0.6", "xtend": "^4.0.1" }, diff --git a/ui/app/components/copyable.js b/ui/app/components/copyable.js new file mode 100644 index 000000000..a4f6f4bc6 --- /dev/null +++ b/ui/app/components/copyable.js @@ -0,0 +1,46 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits + +const Tooltip = require('./tooltip') +const copyToClipboard = require('copy-to-clipboard') + +module.exports = Copyable + +inherits(Copyable, Component) +function Copyable () { + Component.call(this) + this.state = { + copied: false, + } +} + +Copyable.prototype.render = function () { + const props = this.props + const state = this.state + const { value, children } = props + const { copied } = state + + return h(Tooltip, { + title: copied ? 'Copied!' : 'Copy', + position: 'bottom', + }, h('span', { + style: { + cursor: 'pointer', + }, + onClick: (event) => { + event.preventDefault() + event.stopPropagation() + copyToClipboard(value) + this.debounceRestore() + }, + }, children)) +} + +Copyable.prototype.debounceRestore = function () { + this.setState({ copied: true }) + clearTimeout(this.timeout) + this.timeout = setTimeout(() => { + this.setState({ copied: false }) + }, 850) +} diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js index 3e44d83af..43bb7ab22 100644 --- a/ui/app/components/ens-input.js +++ b/ui/app/components/ens-input.js @@ -21,6 +21,7 @@ EnsInput.prototype.render = function () { const opts = extend(props, { list: 'addresses', onChange: () => { + this.setState({ ensResolution: '0x0000000000000000000000000000000000000000' }) const network = this.props.network const networkHasEnsSupport = getNetworkEnsSupport(network) if (!networkHasEnsSupport) return @@ -95,12 +96,14 @@ EnsInput.prototype.lookupEnsName = function () { log.info(`ENS attempting to resolve name: ${recipient}`) this.ens.lookup(recipient.trim()) .then((address) => { + if (address === '0x0000000000000000000000000000000000000000') throw new Error('No address has been set for this name.') if (address !== ensResolution) { this.setState({ loadingEns: false, ensResolution: address, nickname: recipient.trim(), hoverText: address + '\nClick to Copy', + ensFailure: false, }) } }) @@ -108,6 +111,7 @@ EnsInput.prototype.lookupEnsName = function () { log.error(reason) return this.setState({ loadingEns: false, + ensResolution: '0x0000000000000000000000000000000000000000', ensFailure: true, hoverText: reason.message, }) diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 0847a8d4c..4b1a00eca 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -7,10 +7,10 @@ const clone = require('clone') const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const hexToBn = require('../../../app/scripts/lib/hex-to-bn') - +const util = require('../util') const MiniAccountPanel = require('./mini-account-panel') +const Copyable = require('./copyable') const EthBalance = require('./eth-balance') -const util = require('../util') const addressSummary = util.addressSummary const nameForAddress = require('../../lib/contract-namer') const BNInput = require('./bn-as-decimal-input') @@ -44,6 +44,9 @@ PendingTx.prototype.render = function () { const account = props.accounts[address] const balance = account ? account.balance : '0x0' + // recipient check + const isValidAddress = !txParams.to || util.isValidAddress(txParams.to) + // Gas const gas = txParams.gas const gasBn = hexToBn(gas) @@ -94,11 +97,16 @@ PendingTx.prototype.render = function () { fontFamily: 'Montserrat Bold, Montserrat, sans-serif', }, }, identity.name), - h('span.font-small', { - style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', - }, - }, addressSummary(address, 6, 4, false)), + + h(Copyable, { + value: ethUtil.toChecksumAddress(address), + }, [ + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, addressSummary(address, 6, 4, false)), + ]), h('span.font-small', { style: { @@ -262,6 +270,15 @@ PendingTx.prototype.render = function () { }, 'Transaction Error. Exception thrown in contract code.') : null, + !isValidAddress ? + h('.error', { + style: { + marginLeft: 50, + fontSize: '0.9em', + }, + }, 'Recipient address is invalid. Sending this transaction will result in a loss of ETH.') + : null, + insufficientBalance ? h('span.error', { style: { @@ -299,7 +316,7 @@ PendingTx.prototype.render = function () { type: 'submit', value: 'ACCEPT', style: { marginLeft: '10px' }, - disabled: insufficientBalance || !this.state.valid, + disabled: insufficientBalance || !this.state.valid || !isValidAddress, }), h('button.cancel.btn-red', { @@ -323,16 +340,23 @@ PendingTx.prototype.miniAccountPanelForRecipient = function () { imageSeed: txParams.to, picOrder: 'left', }, [ + h('span.font-small', { style: { fontFamily: 'Montserrat Bold, Montserrat, sans-serif', }, }, nameForAddress(txParams.to, props.identities)), - h('span.font-small', { - style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', - }, - }, addressSummary(txParams.to, 6, 4, false)), + + h(Copyable, { + value: ethUtil.toChecksumAddress(txParams.to), + }, [ + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, addressSummary(txParams.to, 6, 4, false)), + ]), + ]) } else { return h(MiniAccountPanel, { diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index c002019e2..747d3ce2b 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -49,6 +49,7 @@ ConfirmTxScreen.prototype.render = function () { var txParams = txData.params || {} var isNotification = isPopupOrNotification() === 'notification' + log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`) if (unconfTxList.length === 0) return h(Loading, { isLoading: true }) diff --git a/ui/app/util.js b/ui/app/util.js index 7a56bf6a0..ac3f42c6b 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -61,6 +61,7 @@ function miniAddressSummary (address) { function isValidAddress (address) { var prefixed = ethUtil.addHexPrefix(address) + if (address === '0x0000000000000000000000000000000000000000') return false return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed) }