mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Merge branch 'uat' of https://github.com/MetaMask/metamask-extension into cb-254
This commit is contained in:
commit
ecc39c5a7a
36
CHANGELOG.md
36
CHANGELOG.md
@ -2,6 +2,42 @@
|
||||
|
||||
## Current Master
|
||||
|
||||
## 3.13.5 2018-1-16
|
||||
|
||||
- Estimating gas limit for simple ether sends now faster & cheaper, by avoiding VM usage on recipients with no code.
|
||||
- Add an extra px to address for Firefox clipping.
|
||||
- Fix Firefox scrollbar.
|
||||
- Open metamask popup for transaction confirmation before gas estimation finishes and add a loading screen over transaction confirmation.
|
||||
- Fix bug that prevented eth_signTypedData from signing bytes.
|
||||
- Further improve gas price estimation.
|
||||
|
||||
## 3.13.4 2018-1-9
|
||||
|
||||
- Remove recipient field if application initializes a tx with an empty string, or 0x, and tx data. Throw an error with the same condition, but without tx data.
|
||||
- Improve gas price suggestion to be closer to the lowest that will be accepted.
|
||||
- Throw an error if a application tries to submit a tx whose value is a decimal, and inform that it should be in wei.
|
||||
- Fix bug that prevented updating custom token details.
|
||||
- No longer mark long-pending transactions as failed, since we now have button to retry with higher gas.
|
||||
- Fix rounding error when specifying an ether amount that has too much precision.
|
||||
- Fix bug where incorrectly inputting seed phrase would prevent any future attempts from succeeding.
|
||||
|
||||
## 3.13.3 2017-12-14
|
||||
|
||||
- Show tokens that are held that have no balance.
|
||||
- Reduce load on Infura by using a new block polling endpoint.
|
||||
|
||||
## 3.13.2 2017-12-9
|
||||
|
||||
- Reduce new block polling interval to 8000 ms, to ease server load.
|
||||
|
||||
## 3.13.1 2017-12-7
|
||||
|
||||
- Allow Dapps to specify a transaction nonce, allowing dapps to propose resubmit and force-cancel transactions.
|
||||
|
||||
## 3.13.0 2017-12-7
|
||||
|
||||
- Allow resubmitting transactions that are taking long to complete.
|
||||
|
||||
## 3.12.1 2017-11-29
|
||||
|
||||
- Fix bug where a user could be shown two different seed phrases.
|
||||
|
@ -1,10 +1,10 @@
|
||||
# MetaMask Plugin
|
||||
# MetaMask Browser Extension
|
||||
[![Build Status](https://circleci.com/gh/MetaMask/metamask-extension.svg?style=shield&circle-token=a1ddcf3cd38e29267f254c9c59d556d513e3a1fd)](https://circleci.com/gh/MetaMask/metamask-extension) [![Coverage Status](https://coveralls.io/repos/github/MetaMask/metamask-extension/badge.svg?branch=master)](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [![Greenkeeper badge](https://badges.greenkeeper.io/MetaMask/metamask-extension.svg)](https://greenkeeper.io/) [![Stories in Ready](https://badge.waffle.io/MetaMask/metamask-extension.png?label=in%20progress&title=waffle.io)](http://waffle.io/MetaMask/metamask-extension)
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
If you're a user seeking support, [here is our support site](http://metamask.consensyssupport.happyfox.com).
|
||||
If you're a user seeking support, [here is our support site](https://metamask.helpscoutdocs.com/).
|
||||
|
||||
## Developing Compatible Dapps
|
||||
|
||||
|
10
app/_locales/ko/messages.json
Normal file
10
app/_locales/ko/messages.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"appName": {
|
||||
"message": "MetaMask",
|
||||
"description": "The name of the application"
|
||||
},
|
||||
"appDescription": {
|
||||
"message": "이더리움 계좌 관리",
|
||||
"description": "The description of the application"
|
||||
}
|
||||
}
|
BIN
app/images/coinbase logo.png
Normal file
BIN
app/images/coinbase logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
128
app/images/metamask-fox.svg
Normal file
128
app/images/metamask-fox.svg
Normal file
@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:ev="http://www.w3.org/2001/xml-events"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 318.6 318.6"
|
||||
style="enable-background:new 0 0 318.6 318.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#161616;stroke:#161616;}
|
||||
.st1{fill:#E4761B;stroke:#E4761B;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st2{fill:#763D16;stroke:#763D16;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st3{fill:#F6851B;stroke:#F6851B;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st4{fill:#E2761B;stroke:#E2761B;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st5{fill:#CD6116;stroke:#CD6116;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st6{fill:#C0AD9E;stroke:#C0AD9E;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st7{fill:#D7C1B3;stroke:#D7C1B3;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st8{fill:#E4751F;stroke:#E4751F;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st9{fill:#233447;stroke:#233447;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st10{fill:#161616;stroke:#161616;stroke-linecap:round;stroke-linejoin:round;}
|
||||
</style>
|
||||
<polygon class="st0" points="277.3,145.6 272.3,142 280.3,134.7 274.2,129.9 282.2,123.8 276.9,119.8 285.3,79 272.7,41.1
|
||||
191.6,71.4 124.1,71.4 43,41.1 30.4,79 38.9,119.8 33.5,123.8 41.5,129.9 35.4,134.7 43.4,142 38.4,145.6 49.9,159.1 32.5,213.3
|
||||
48.6,268.6 105.3,253 116.3,262 138.7,277.5 177,277.5 199.4,262 210.4,253 267.1,268.6 283.3,213.3 265.8,159.1 "/>
|
||||
<g>
|
||||
<polygon class="st1" points="105.3,253 48.6,268.6 32.5,213.3 "/>
|
||||
<polygon class="st1" points="283.3,213.3 267.1,268.6 210.4,253 "/>
|
||||
<polygon class="st2" points="265.8,159.1 213.5,143.8 231.8,139 "/>
|
||||
<polygon class="st2" points="49.9,159.1 84,139 102.2,143.8 "/>
|
||||
<polygon class="st2" points="43.4,142 41.5,129.9 84,139 "/>
|
||||
<polygon class="st2" points="272.3,142 231.8,139 274.2,129.9 "/>
|
||||
<polygon class="st2" points="272.3,142 265.8,159.1 231.8,139 "/>
|
||||
<polygon class="st2" points="43.4,142 84,139 49.9,159.1 "/>
|
||||
<polygon class="st2" points="231.8,139 276.9,119.8 274.2,129.9 "/>
|
||||
<polygon class="st2" points="84,139 41.5,129.9 38.9,119.8 "/>
|
||||
<polygon class="st3" points="124.1,71.4 191.6,71.4 176.5,112.5 "/>
|
||||
<polygon class="st3" points="176.5,112.5 139.2,112.5 124.1,71.4 "/>
|
||||
<polygon class="st2" points="276.9,119.8 231.8,139 231,87.4 "/>
|
||||
<polygon class="st2" points="102.2,143.8 84,139 84.7,87.4 "/>
|
||||
<polygon class="st2" points="84.7,87.4 84,139 38.9,119.8 "/>
|
||||
<polygon class="st2" points="231,87.4 231.8,139 213.5,143.8 "/>
|
||||
<polygon class="st1" points="139.2,112.5 43,41.1 124.1,71.4 "/>
|
||||
<polygon class="st4" points="272.7,41.1 176.5,112.5 191.6,71.4 "/>
|
||||
<polygon class="st1" points="210.4,253 236.9,213.3 283.3,213.3 "/>
|
||||
<polygon class="st1" points="32.5,213.3 78.9,213.3 105.3,253 "/>
|
||||
<polygon class="st3" points="229.3,167.7 283.3,213.3 236.9,213.3 "/>
|
||||
<polygon class="st3" points="86.4,167.7 32.5,213.3 49.9,159.1 "/>
|
||||
<polygon class="st3" points="78.9,213.3 32.5,213.3 86.4,167.7 "/>
|
||||
<polygon class="st3" points="229.3,167.7 265.8,159.1 283.3,213.3 "/>
|
||||
<polygon class="st2" points="84.7,87.4 139.2,112.5 102.2,143.8 "/>
|
||||
<polygon class="st2" points="213.5,143.8 176.5,112.5 231,87.4 "/>
|
||||
<polygon class="st2" points="265.8,159.1 272.3,142 277.3,145.6 "/>
|
||||
<polygon class="st2" points="49.9,159.1 38.4,145.6 43.4,142 "/>
|
||||
<polygon class="st2" points="272.3,142 274.2,129.9 280.3,134.7 "/>
|
||||
<polygon class="st2" points="43.4,142 35.4,134.7 41.5,129.9 "/>
|
||||
<polygon class="st2" points="33.5,123.8 38.9,119.8 41.5,129.9 "/>
|
||||
<polygon class="st2" points="282.2,123.8 274.2,129.9 276.9,119.8 "/>
|
||||
<polygon class="st3" points="49.9,159.1 102.2,143.8 86.4,167.7 "/>
|
||||
<polygon class="st3" points="265.8,159.1 229.3,167.7 213.5,143.8 "/>
|
||||
<polygon class="st2" points="38.9,119.8 30.4,79 84.7,87.4 "/>
|
||||
<polygon class="st2" points="231,87.4 285.3,79 276.9,119.8 "/>
|
||||
<polygon class="st1" points="102.2,143.8 139.2,112.5 142.6,170.2 "/>
|
||||
<polygon class="st1" points="213.5,143.8 229.3,167.7 173.1,170.2 "/>
|
||||
<polygon class="st1" points="173.1,170.2 176.5,112.5 213.5,143.8 "/>
|
||||
<polygon class="st1" points="142.6,170.2 86.4,167.7 102.2,143.8 "/>
|
||||
<polygon class="st2" points="272.7,41.1 285.3,79 231,87.4 "/>
|
||||
<polygon class="st2" points="43,41.1 139.2,112.5 84.7,87.4 "/>
|
||||
<polygon class="st2" points="231,87.4 176.5,112.5 272.7,41.1 "/>
|
||||
<polygon class="st2" points="84.7,87.4 30.4,79 43,41.1 "/>
|
||||
<polygon class="st5" points="105.3,253 78.9,213.3 110,213.7 "/>
|
||||
<polygon class="st5" points="210.4,253 205.7,213.7 236.9,213.3 "/>
|
||||
<polygon class="st3" points="173.1,170.2 142.6,170.2 139.2,112.5 "/>
|
||||
<polygon class="st3" points="139.2,112.5 176.5,112.5 173.1,170.2 "/>
|
||||
<polygon class="st6" points="116.3,262 105.3,253 136.8,267.9 "/>
|
||||
<polygon class="st6" points="178.9,267.9 210.4,253 199.4,262 "/>
|
||||
<polygon class="st7" points="136.6,258.6 136.8,267.9 105.3,253 "/>
|
||||
<polygon class="st7" points="179.2,258.6 210.4,253 178.9,267.9 "/>
|
||||
<polygon class="st3" points="86.4,167.7 110,213.7 78.9,213.3 "/>
|
||||
<polygon class="st3" points="236.9,213.3 205.7,213.7 229.3,167.7 "/>
|
||||
<polygon class="st8" points="86.4,167.7 109.2,190.8 110,213.7 "/>
|
||||
<polygon class="st8" points="229.3,167.7 205.7,213.7 206.6,190.8 "/>
|
||||
<polygon class="st7" points="105.3,253 139.2,236.5 136.6,258.6 "/>
|
||||
<polygon class="st7" points="210.4,253 179.2,258.6 176.5,236.5 "/>
|
||||
<polygon class="st1" points="139.2,236.5 105.3,253 110,213.7 "/>
|
||||
<polygon class="st1" points="176.5,236.5 205.7,213.7 210.4,253 "/>
|
||||
<polygon class="st5" points="173.1,170.2 229.3,167.7 206.6,190.8 "/>
|
||||
<polygon class="st5" points="109.2,190.8 86.4,167.7 142.6,170.2 "/>
|
||||
<polygon class="st5" points="142.6,170.2 129.1,181.7 109.2,190.8 "/>
|
||||
<polygon class="st5" points="206.6,190.8 186.6,181.7 173.1,170.2 "/>
|
||||
<polygon class="st3" points="205.7,213.7 178.3,199.1 206.6,190.8 "/>
|
||||
<polygon class="st3" points="110,213.7 109.2,190.8 137.4,199.1 "/>
|
||||
<polygon class="st9" points="137.4,199.1 109.2,190.8 129.1,181.7 "/>
|
||||
<polygon class="st9" points="178.3,199.1 186.6,181.7 206.6,190.8 "/>
|
||||
<polygon class="st5" points="186.6,181.7 178.3,199.1 173.1,170.2 "/>
|
||||
<polygon class="st5" points="129.1,181.7 142.6,170.2 137.4,199.1 "/>
|
||||
<polygon class="st6" points="199.4,262 177,277.5 178.9,267.9 "/>
|
||||
<polygon class="st6" points="136.8,267.9 138.7,277.5 116.3,262 "/>
|
||||
<polygon class="st4" points="178.3,199.1 171.8,188.4 173.1,170.2 "/>
|
||||
<polygon class="st8" points="137.4,199.1 142.6,170.2 143.9,188.4 "/>
|
||||
<polygon class="st3" points="173.1,170.2 171.8,188.4 143.9,188.4 "/>
|
||||
<polygon class="st3" points="143.9,188.4 142.6,170.2 173.1,170.2 "/>
|
||||
<polygon class="st3" points="178.3,199.1 205.7,213.7 176.5,236.5 "/>
|
||||
<polygon class="st3" points="139.2,236.5 110,213.7 137.4,199.1 "/>
|
||||
<polygon class="st3" points="137.4,199.1 144,233.2 139.2,236.5 "/>
|
||||
<polygon class="st3" points="176.5,236.5 171.7,233.2 178.3,199.1 "/>
|
||||
<polygon class="st8" points="171.8,188.4 178.3,199.1 171.7,233.2 "/>
|
||||
<polygon class="st8" points="143.9,188.4 144,233.2 137.4,199.1 "/>
|
||||
<polygon class="st3" points="143.9,188.4 171.8,188.4 171.7,233.2 "/>
|
||||
<polygon class="st3" points="171.7,233.2 144,233.2 143.9,188.4 "/>
|
||||
<polygon class="st6" points="179.2,258.6 178.9,267.9 177,277.5 "/>
|
||||
<polygon class="st6" points="138.7,277.5 136.8,267.9 136.6,258.6 "/>
|
||||
<polygon class="st6" points="136.6,258.6 139,256.4 138.7,277.5 "/>
|
||||
<polygon class="st6" points="177,277.5 176.7,256.4 179.2,258.6 "/>
|
||||
<polygon class="st6" points="138.7,277.5 139,256.4 176.7,256.4 "/>
|
||||
<polygon class="st6" points="176.7,256.4 177,277.5 138.7,277.5 "/>
|
||||
<polygon class="st10" points="176.5,236.5 179.2,258.6 176.7,256.4 "/>
|
||||
<polygon class="st10" points="139,256.4 136.6,258.6 139.2,236.5 "/>
|
||||
<polygon class="st10" points="139.2,236.5 140.7,241.2 139,256.4 "/>
|
||||
<polygon class="st10" points="176.7,256.4 175,241.2 176.5,236.5 "/>
|
||||
<polygon class="st10" points="143.7,237.7 140.7,241.2 139.2,236.5 "/>
|
||||
<polygon class="st10" points="176.5,236.5 175,241.2 172,237.7 "/>
|
||||
<polygon class="st10" points="172,237.7 171.7,233.2 176.5,236.5 "/>
|
||||
<polygon class="st10" points="139.2,236.5 144,233.2 143.7,237.7 "/>
|
||||
<polygon class="st10" points="171.7,233.2 172,237.7 143.7,237.7 "/>
|
||||
<polygon class="st10" points="143.7,237.7 144,233.2 171.7,233.2 "/>
|
||||
<polygon class="st10" points="140.7,241.2 175,241.2 176.7,256.4 "/>
|
||||
<polygon class="st10" points="176.7,256.4 139,256.4 140.7,241.2 "/>
|
||||
<polygon class="st10" points="140.7,241.2 143.7,237.7 172,237.7 "/>
|
||||
<polygon class="st10" points="172,237.7 175,241.2 140.7,241.2 "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.6 KiB |
21
app/images/popout.svg
Normal file
21
app/images/popout.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>popout</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<polygon id="path-1" points="-0.00035 0 10.9999 0 10.9999 10.9997 -0.00035 10.9997"></polygon>
|
||||
</defs>
|
||||
<g id="MetaMascara-Mobile---structured-TOKEN" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(-327.000000, -96.000000)">
|
||||
<g id="popout" transform="translate(327.000000, 96.000000)">
|
||||
<g id="Group-3" transform="translate(11.000000, 0.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<g id="Clip-2"></g>
|
||||
<path d="M10.9229,0.6177 C10.8209,0.3737 10.6269,0.1787 10.3819,0.0767 C10.2599,0.0267 10.1309,-0.0003 9.9999,-0.0003 L3.9999,-0.0003 C3.4479,-0.0003 2.9999,0.4477 2.9999,0.9997 C2.9999,1.5527 3.4479,1.9997 3.9999,1.9997 L7.5859,1.9997 L0.2929,9.2927 C-0.0981,9.6837 -0.0981,10.3167 0.2929,10.7067 C0.4879,10.9027 0.7439,10.9997 0.9999,10.9997 C1.2559,10.9997 1.5119,10.9027 1.7069,10.7067 L8.9999,3.4137 L8.9999,6.9997 C8.9999,7.5527 9.4479,7.9997 9.9999,7.9997 C10.5519,7.9997 10.9999,7.5527 10.9999,6.9997 L10.9999,0.9997 C10.9999,0.8697 10.9739,0.7407 10.9229,0.6177" id="Fill-1" fill="#4A4A4A" mask="url(#mask-2)"></path>
|
||||
</g>
|
||||
<path d="M19,10 C18.448,10 18,10.448 18,11 L18,19 C18,19.551 17.551,20 17,20 L3,20 C2.449,20 2,19.551 2,19 L2,5 C2,4.449 2.449,4 3,4 L11,4 C11.552,4 12,3.552 12,3 C12,2.448 11.552,2 11,2 L3,2 C1.346,2 0,3.346 0,5 L0,19 C0,20.654 1.346,22 3,22 L17,22 C18.654,22 20,20.654 20,19 L20,11 C20,10.448 19.552,10 19,10" id="Fill-4" fill="#4A4A4A"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/images/shapeshift logo.png
Normal file
BIN
app/images/shapeshift logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "MetaMask",
|
||||
"short_name": "Metamask",
|
||||
"version": "4.0.4",
|
||||
"version": "4.0.9",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "Ethereum Browser Extension",
|
||||
|
@ -4,6 +4,15 @@ const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask'
|
||||
const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask'
|
||||
const LOCALHOST_RPC_URL = 'http://localhost:8545'
|
||||
|
||||
const MAINET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2'
|
||||
const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2'
|
||||
const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2'
|
||||
const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2'
|
||||
|
||||
const DEFAULT_RPC = 'rinkeby'
|
||||
const OLD_UI_NETWORK_TYPE = 'network'
|
||||
const BETA_UI_NETWORK_TYPE = 'networkBeta'
|
||||
|
||||
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
|
||||
|
||||
module.exports = {
|
||||
@ -14,9 +23,22 @@ module.exports = {
|
||||
kovan: KOVAN_RPC_URL,
|
||||
rinkeby: RINKEBY_RPC_URL,
|
||||
},
|
||||
// Used for beta UI
|
||||
networkBeta: {
|
||||
localhost: LOCALHOST_RPC_URL,
|
||||
mainnet: MAINET_RPC_URL_BETA,
|
||||
ropsten: ROPSTEN_RPC_URL_BETA,
|
||||
kovan: KOVAN_RPC_URL_BETA,
|
||||
rinkeby: RINKEBY_RPC_URL_BETA,
|
||||
},
|
||||
networkNames: {
|
||||
3: 'Ropsten',
|
||||
4: 'Rinkeby',
|
||||
42: 'Kovan',
|
||||
},
|
||||
enums: {
|
||||
DEFAULT_RPC,
|
||||
OLD_UI_NETWORK_TYPE,
|
||||
BETA_UI_NETWORK_TYPE,
|
||||
},
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ function logStreamDisconnectWarning (remoteLabel, err) {
|
||||
}
|
||||
|
||||
function shouldInjectWeb3 () {
|
||||
return doctypeCheck() || suffixCheck()
|
||||
return doctypeCheck() && suffixCheck() && documentElementCheck()
|
||||
}
|
||||
|
||||
function doctypeCheck () {
|
||||
@ -104,7 +104,7 @@ function doctypeCheck () {
|
||||
if (doctype) {
|
||||
return doctype.name === 'html'
|
||||
} else {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,6 +121,14 @@ function suffixCheck () {
|
||||
return true
|
||||
}
|
||||
|
||||
function documentElementCheck () {
|
||||
var documentElement = document.documentElement.nodeName
|
||||
if (documentElement) {
|
||||
return documentElement.toLowerCase() === 'html'
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function redirectToPhishingWarning () {
|
||||
console.log('MetaMask - redirecting to phishing warning')
|
||||
window.location.href = 'https://metamask.io/phishing.html'
|
||||
|
@ -57,3 +57,4 @@ class BlacklistController {
|
||||
}
|
||||
|
||||
module.exports = BlacklistController
|
||||
|
||||
|
@ -1,18 +1,26 @@
|
||||
const assert = require('assert')
|
||||
const EventEmitter = require('events')
|
||||
const createMetamaskProvider = require('web3-provider-engine/zero.js')
|
||||
const SubproviderFromProvider = require('web3-provider-engine/subproviders/web3.js')
|
||||
const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider')
|
||||
const ObservableStore = require('obs-store')
|
||||
const ComposedStore = require('obs-store/lib/composed')
|
||||
const extend = require('xtend')
|
||||
const EthQuery = require('eth-query')
|
||||
const createEventEmitterProxy = require('../lib/events-proxy.js')
|
||||
const RPC_ADDRESS_LIST = require('../config.js').network
|
||||
const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby']
|
||||
const networkConfig = require('../config.js')
|
||||
const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums
|
||||
const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
|
||||
|
||||
module.exports = class NetworkController extends EventEmitter {
|
||||
|
||||
constructor (config) {
|
||||
super()
|
||||
|
||||
this._networkEndpointVersion = OLD_UI_NETWORK_TYPE
|
||||
this._networkEndpoints = this.getNetworkEndpoints(OLD_UI_NETWORK_TYPE)
|
||||
this._defaultRpc = this._networkEndpoints[DEFAULT_RPC]
|
||||
|
||||
config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
|
||||
this.networkStore = new ObservableStore('loading')
|
||||
this.providerStore = new ObservableStore(config.provider)
|
||||
@ -22,10 +30,32 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
this.on('networkDidChange', this.lookupNetwork)
|
||||
}
|
||||
|
||||
async setNetworkEndpoints (version) {
|
||||
if (version === this._networkEndpointVersion) {
|
||||
return
|
||||
}
|
||||
|
||||
this._networkEndpointVersion = version
|
||||
this._networkEndpoints = this.getNetworkEndpoints(version)
|
||||
this._defaultRpc = this._networkEndpoints[DEFAULT_RPC]
|
||||
const { type } = this.getProviderConfig()
|
||||
|
||||
return this.setProviderType(type, true)
|
||||
}
|
||||
|
||||
getNetworkEndpoints (version = OLD_UI_NETWORK_TYPE) {
|
||||
return networkConfig[version]
|
||||
}
|
||||
|
||||
initializeProvider (_providerParams) {
|
||||
this._baseProviderParams = _providerParams
|
||||
const rpcUrl = this.getCurrentRpcAddress()
|
||||
this._configureStandardProvider({ rpcUrl })
|
||||
const { type, rpcTarget } = this.providerStore.getState()
|
||||
// map rpcTarget to rpcUrl
|
||||
const opts = {
|
||||
type,
|
||||
rpcUrl: rpcTarget,
|
||||
}
|
||||
this._configureProvider(opts)
|
||||
this._proxy.on('block', this._logBlock.bind(this))
|
||||
this._proxy.on('error', this.verifyNetwork.bind(this))
|
||||
this.ethQuery = new EthQuery(this._proxy)
|
||||
@ -76,14 +106,17 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
return this.getRpcAddressForType(provider.type)
|
||||
}
|
||||
|
||||
async setProviderType (type) {
|
||||
async setProviderType (type, forceUpdate = false) {
|
||||
assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`)
|
||||
// skip if type already matches
|
||||
if (type === this.getProviderConfig().type) return
|
||||
if (type === this.getProviderConfig().type && !forceUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
const rpcTarget = this.getRpcAddressForType(type)
|
||||
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
|
||||
this.providerStore.updateState({ type, rpcTarget })
|
||||
this._switchNetwork({ rpcUrl: rpcTarget })
|
||||
this._switchNetwork({ type })
|
||||
}
|
||||
|
||||
getProviderConfig () {
|
||||
@ -91,22 +124,67 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
}
|
||||
|
||||
getRpcAddressForType (type, provider = this.getProviderConfig()) {
|
||||
if (RPC_ADDRESS_LIST[type]) return RPC_ADDRESS_LIST[type]
|
||||
return provider && provider.rpcTarget ? provider.rpcTarget : DEFAULT_RPC
|
||||
if (this._networkEndpoints[type]) {
|
||||
return this._networkEndpoints[type]
|
||||
}
|
||||
|
||||
return provider && provider.rpcTarget ? provider.rpcTarget : this._defaultRpc
|
||||
}
|
||||
|
||||
//
|
||||
// Private
|
||||
//
|
||||
|
||||
_switchNetwork (providerParams) {
|
||||
_switchNetwork (opts) {
|
||||
this.setNetworkState('loading')
|
||||
this._configureStandardProvider(providerParams)
|
||||
this._configureProvider(opts)
|
||||
this.emit('networkDidChange')
|
||||
}
|
||||
|
||||
_configureStandardProvider (_providerParams) {
|
||||
const providerParams = extend(this._baseProviderParams, _providerParams)
|
||||
_configureProvider (opts) {
|
||||
// type-based rpc endpoints
|
||||
const { type } = opts
|
||||
if (type) {
|
||||
// type-based infura rpc endpoints
|
||||
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
|
||||
opts.rpcUrl = this.getRpcAddressForType(type)
|
||||
if (isInfura) {
|
||||
this._configureInfuraProvider(opts)
|
||||
// other type-based rpc endpoints
|
||||
} else {
|
||||
this._configureStandardProvider(opts)
|
||||
}
|
||||
// url-based rpc endpoints
|
||||
} else {
|
||||
this._configureStandardProvider(opts)
|
||||
}
|
||||
}
|
||||
|
||||
_configureInfuraProvider (opts) {
|
||||
log.info('_configureInfuraProvider', opts)
|
||||
const infuraProvider = createInfuraProvider({
|
||||
network: opts.type,
|
||||
})
|
||||
const infuraSubprovider = new SubproviderFromProvider(infuraProvider)
|
||||
const providerParams = extend(this._baseProviderParams, {
|
||||
rpcUrl: opts.rpcUrl,
|
||||
engineParams: {
|
||||
pollingInterval: 8000,
|
||||
blockTrackerProvider: infuraProvider,
|
||||
},
|
||||
dataSubprovider: infuraSubprovider,
|
||||
})
|
||||
const provider = createMetamaskProvider(providerParams)
|
||||
this._setProvider(provider)
|
||||
}
|
||||
|
||||
_configureStandardProvider ({ rpcUrl }) {
|
||||
const providerParams = extend(this._baseProviderParams, {
|
||||
rpcUrl,
|
||||
engineParams: {
|
||||
pollingInterval: 8000,
|
||||
},
|
||||
})
|
||||
const provider = createMetamaskProvider(providerParams)
|
||||
this._setProvider(provider)
|
||||
}
|
||||
|
@ -36,22 +36,24 @@ class PreferencesController {
|
||||
return this.store.getState().selectedAddress
|
||||
}
|
||||
|
||||
addToken (rawAddress, symbol, decimals) {
|
||||
async addToken (rawAddress, symbol, decimals) {
|
||||
const address = normalizeAddress(rawAddress)
|
||||
const newEntry = { address, symbol, decimals }
|
||||
|
||||
const tokens = this.store.getState().tokens
|
||||
const previousIndex = tokens.find((token, index) => {
|
||||
const previousEntry = tokens.find((token, index) => {
|
||||
return token.address === address
|
||||
})
|
||||
const previousIndex = tokens.indexOf(previousEntry)
|
||||
|
||||
if (previousIndex) {
|
||||
if (previousEntry) {
|
||||
tokens[previousIndex] = newEntry
|
||||
} else {
|
||||
tokens.push(newEntry)
|
||||
}
|
||||
|
||||
this.store.updateState({ tokens })
|
||||
|
||||
return Promise.resolve(tokens)
|
||||
}
|
||||
|
||||
|
110
app/scripts/controllers/recent-blocks.js
Normal file
110
app/scripts/controllers/recent-blocks.js
Normal file
@ -0,0 +1,110 @@
|
||||
const ObservableStore = require('obs-store')
|
||||
const extend = require('xtend')
|
||||
const BN = require('ethereumjs-util').BN
|
||||
const EthQuery = require('eth-query')
|
||||
|
||||
class RecentBlocksController {
|
||||
|
||||
constructor (opts = {}) {
|
||||
const { blockTracker, provider } = opts
|
||||
this.blockTracker = blockTracker
|
||||
this.ethQuery = new EthQuery(provider)
|
||||
this.historyLength = opts.historyLength || 40
|
||||
|
||||
const initState = extend({
|
||||
recentBlocks: [],
|
||||
}, opts.initState)
|
||||
this.store = new ObservableStore(initState)
|
||||
|
||||
this.blockTracker.on('block', this.processBlock.bind(this))
|
||||
this.backfill()
|
||||
}
|
||||
|
||||
resetState () {
|
||||
this.store.updateState({
|
||||
recentBlocks: [],
|
||||
})
|
||||
}
|
||||
|
||||
processBlock (newBlock) {
|
||||
const block = this.mapTransactionsToPrices(newBlock)
|
||||
|
||||
const state = this.store.getState()
|
||||
state.recentBlocks.push(block)
|
||||
|
||||
while (state.recentBlocks.length > this.historyLength) {
|
||||
state.recentBlocks.shift()
|
||||
}
|
||||
|
||||
this.store.updateState(state)
|
||||
}
|
||||
|
||||
backfillBlock (newBlock) {
|
||||
const block = this.mapTransactionsToPrices(newBlock)
|
||||
|
||||
const state = this.store.getState()
|
||||
|
||||
if (state.recentBlocks.length < this.historyLength) {
|
||||
state.recentBlocks.unshift(block)
|
||||
}
|
||||
|
||||
this.store.updateState(state)
|
||||
}
|
||||
|
||||
mapTransactionsToPrices (newBlock) {
|
||||
const block = extend(newBlock, {
|
||||
gasPrices: newBlock.transactions.map((tx) => {
|
||||
return tx.gasPrice
|
||||
}),
|
||||
})
|
||||
delete block.transactions
|
||||
return block
|
||||
}
|
||||
|
||||
async backfill() {
|
||||
this.blockTracker.once('block', async (block) => {
|
||||
let blockNum = block.number
|
||||
let recentBlocks
|
||||
let state = this.store.getState()
|
||||
recentBlocks = state.recentBlocks
|
||||
|
||||
while (recentBlocks.length < this.historyLength) {
|
||||
try {
|
||||
let blockNumBn = new BN(blockNum.substr(2), 16)
|
||||
const newNum = blockNumBn.subn(1).toString(10)
|
||||
const newBlock = await this.getBlockByNumber(newNum)
|
||||
|
||||
if (newBlock) {
|
||||
this.backfillBlock(newBlock)
|
||||
blockNum = newBlock.number
|
||||
}
|
||||
|
||||
state = this.store.getState()
|
||||
recentBlocks = state.recentBlocks
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
}
|
||||
await this.wait()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async wait () {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, 100)
|
||||
})
|
||||
}
|
||||
|
||||
async getBlockByNumber (number) {
|
||||
const bn = new BN(number)
|
||||
return new Promise((resolve, reject) => {
|
||||
this.ethQuery.getBlockByNumber('0x' + bn.toString(16), true, (err, block) => {
|
||||
if (err) reject(err)
|
||||
resolve(block)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = RecentBlocksController
|
@ -32,6 +32,7 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
this.provider = opts.provider
|
||||
this.blockTracker = opts.blockTracker
|
||||
this.signEthTx = opts.signTransaction
|
||||
this.getGasPrice = opts.getGasPrice
|
||||
|
||||
this.memStore = new ObservableStore({})
|
||||
this.query = new EthQuery(this.provider)
|
||||
@ -59,7 +60,6 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
this.pendingTxTracker = new PendingTransactionTracker({
|
||||
provider: this.provider,
|
||||
nonceTracker: this.nonceTracker,
|
||||
retryTimePeriod: 86400000, // Retry 3500 blocks, or about 1 day.
|
||||
publishTransaction: (rawTx) => this.query.sendRawTransaction(rawTx),
|
||||
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
|
||||
getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
|
||||
@ -138,18 +138,19 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
|
||||
async newUnapprovedTransaction (txParams) {
|
||||
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
|
||||
const txMeta = await this.addUnapprovedTransaction(txParams)
|
||||
this.emit('newUnapprovedTx', txMeta)
|
||||
const initialTxMeta = await this.addUnapprovedTransaction(txParams)
|
||||
// listen for tx completion (success, fail)
|
||||
return new Promise((resolve, reject) => {
|
||||
this.txStateManager.once(`${txMeta.id}:finished`, (completedTx) => {
|
||||
switch (completedTx.status) {
|
||||
this.txStateManager.once(`${initialTxMeta.id}:finished`, (finishedTxMeta) => {
|
||||
switch (finishedTxMeta.status) {
|
||||
case 'submitted':
|
||||
return resolve(completedTx.hash)
|
||||
return resolve(finishedTxMeta.hash)
|
||||
case 'rejected':
|
||||
return reject(new Error('MetaMask Tx Signature: User denied transaction signature.'))
|
||||
case 'failed':
|
||||
return reject(new Error(finishedTxMeta.err.message))
|
||||
default:
|
||||
return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(completedTx.txParams)}`))
|
||||
return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -165,11 +166,16 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
status: 'unapproved',
|
||||
metamaskNetworkId: this.getNetwork(),
|
||||
txParams: txParams,
|
||||
loadingDefaults: true,
|
||||
}
|
||||
this.addTx(txMeta)
|
||||
this.emit('newUnapprovedTx', txMeta)
|
||||
// add default tx params
|
||||
await this.addTxDefaults(txMeta)
|
||||
|
||||
txMeta.loadingDefaults = false
|
||||
// save txMeta
|
||||
this.addTx(txMeta)
|
||||
this.txStateManager.updateTx(txMeta)
|
||||
return txMeta
|
||||
}
|
||||
|
||||
@ -177,13 +183,28 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
const txParams = txMeta.txParams
|
||||
// ensure value
|
||||
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
|
||||
const gasPrice = txParams.gasPrice || await this.query.gasPrice()
|
||||
txMeta.nonceSpecified = Boolean(txParams.nonce)
|
||||
let gasPrice = txParams.gasPrice
|
||||
if (!gasPrice) {
|
||||
gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.gasPrice()
|
||||
}
|
||||
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
|
||||
txParams.value = txParams.value || '0x0'
|
||||
// set gasLimit
|
||||
return await this.txGasUtil.analyzeGasUsage(txMeta)
|
||||
}
|
||||
|
||||
async retryTransaction (txId) {
|
||||
this.txStateManager.setTxStatusUnapproved(txId)
|
||||
const txMeta = this.txStateManager.getTx(txId)
|
||||
txMeta.lastGasPrice = txMeta.txParams.gasPrice
|
||||
this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry')
|
||||
}
|
||||
|
||||
async updateTransaction (txMeta) {
|
||||
this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction')
|
||||
}
|
||||
|
||||
async updateAndApproveTransaction (txMeta) {
|
||||
this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
|
||||
await this.approveTransaction(txMeta.id)
|
||||
@ -200,7 +221,12 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
// wait for a nonce
|
||||
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
|
||||
// add nonce to txParams
|
||||
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonceLock.nextNonce.toString(16))
|
||||
const nonce = txMeta.nonceSpecified ? txMeta.txParams.nonce : nonceLock.nextNonce
|
||||
if (nonce > nonceLock.nextNonce) {
|
||||
const message = `Specified nonce may not be larger than account's next valid nonce.`
|
||||
throw new Error(message)
|
||||
}
|
||||
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
|
||||
// add nonce debugging information to txMeta
|
||||
txMeta.nonceDetails = nonceLock.nonceDetails
|
||||
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
|
||||
|
@ -117,8 +117,6 @@ class AccountTracker extends EventEmitter {
|
||||
const query = this._query
|
||||
async.parallel({
|
||||
balance: query.getBalance.bind(query, address),
|
||||
nonce: query.getTransactionCount.bind(query, address),
|
||||
code: query.getCode.bind(query, address),
|
||||
}, cb)
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
|
||||
this.query = new EthQuery(config.provider)
|
||||
this.nonceTracker = config.nonceTracker
|
||||
// default is one day
|
||||
this.retryTimePeriod = config.retryTimePeriod || 86400000
|
||||
this.getPendingTransactions = config.getPendingTransactions
|
||||
this.getCompletedTransactions = config.getCompletedTransactions
|
||||
this.publishTransaction = config.publishTransaction
|
||||
@ -106,12 +105,6 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
|
||||
this.emit('tx:block-update', txMeta, latestBlockNumber)
|
||||
}
|
||||
|
||||
if (Date.now() > txMeta.time + this.retryTimePeriod) {
|
||||
const hours = (this.retryTimePeriod / 3.6e+6).toFixed(1)
|
||||
const err = new Error(`Gave up submitting after ${hours} hours.`)
|
||||
return this.emit('tx:failed', txMeta.id, err)
|
||||
}
|
||||
|
||||
const firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber
|
||||
const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16)
|
||||
|
||||
@ -185,7 +178,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
|
||||
}
|
||||
|
||||
async _checkIfNonceIsTaken (txMeta) {
|
||||
const completed = this.getCompletedTransactions()
|
||||
const address = txMeta.txParams.from
|
||||
const completed = this.getCompletedTransactions(address)
|
||||
const sameNonce = completed.filter((otherMeta) => {
|
||||
return otherMeta.txParams.nonce === txMeta.txParams.nonce
|
||||
})
|
||||
|
@ -4,6 +4,7 @@ const {
|
||||
BnMultiplyByFraction,
|
||||
bnToHex,
|
||||
} = require('./util')
|
||||
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
|
||||
|
||||
/*
|
||||
tx-utils are utility methods for Transaction manager
|
||||
@ -22,7 +23,11 @@ module.exports = class txProvideUtil {
|
||||
try {
|
||||
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
||||
} catch (err) {
|
||||
if (err.message.includes('Transaction execution error.')) {
|
||||
const simulationFailed = (
|
||||
err.message.includes('Transaction execution error.') ||
|
||||
err.message.includes('gas required exceeds allowance or always failing transaction')
|
||||
)
|
||||
if (simulationFailed) {
|
||||
txMeta.simulationFails = true
|
||||
return txMeta
|
||||
}
|
||||
@ -33,14 +38,30 @@ module.exports = class txProvideUtil {
|
||||
|
||||
async estimateTxGas (txMeta, blockGasLimitHex) {
|
||||
const txParams = txMeta.txParams
|
||||
|
||||
// check if gasLimit is already specified
|
||||
txMeta.gasLimitSpecified = Boolean(txParams.gas)
|
||||
// if not, fallback to block gasLimit
|
||||
if (!txMeta.gasLimitSpecified) {
|
||||
|
||||
// if it is, use that value
|
||||
if (txMeta.gasLimitSpecified) {
|
||||
return txParams.gas
|
||||
}
|
||||
|
||||
// if recipient has no code, gas is 21k max:
|
||||
const recipient = txParams.to
|
||||
const hasRecipient = Boolean(recipient)
|
||||
const code = await this.query.getCode(recipient)
|
||||
if (hasRecipient && (!code || code === '0x')) {
|
||||
txParams.gas = SIMPLE_GAS_COST
|
||||
txMeta.simpleSend = true // Prevents buffer addition
|
||||
return SIMPLE_GAS_COST
|
||||
}
|
||||
|
||||
// if not, fall back to block gasLimit
|
||||
const blockGasLimitBN = hexToBn(blockGasLimitHex)
|
||||
const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20)
|
||||
txParams.gas = bnToHex(saferGasLimitBN)
|
||||
}
|
||||
|
||||
// run tx
|
||||
return await this.query.estimateGas(txParams)
|
||||
}
|
||||
@ -51,7 +72,7 @@ module.exports = class txProvideUtil {
|
||||
|
||||
// if gasLimit was specified and doesnt OOG,
|
||||
// use original specified amount
|
||||
if (txMeta.gasLimitSpecified) {
|
||||
if (txMeta.gasLimitSpecified || txMeta.simpleSend) {
|
||||
txMeta.estimatedGas = txParams.gas
|
||||
return
|
||||
}
|
||||
@ -77,8 +98,26 @@ module.exports = class txProvideUtil {
|
||||
}
|
||||
|
||||
async validateTxParams (txParams) {
|
||||
if (('value' in txParams) && txParams.value.indexOf('-') === 0) {
|
||||
this.validateRecipient(txParams)
|
||||
if ('value' in txParams) {
|
||||
const value = txParams.value.toString()
|
||||
if (value.includes('-')) {
|
||||
throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
|
||||
}
|
||||
|
||||
if (value.includes('.')) {
|
||||
throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`)
|
||||
}
|
||||
}
|
||||
}
|
||||
validateRecipient (txParams) {
|
||||
if (txParams.to === '0x') {
|
||||
if (txParams.data) {
|
||||
delete txParams.to
|
||||
} else {
|
||||
throw new Error('Invalid recipient address')
|
||||
}
|
||||
}
|
||||
return txParams
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +187,10 @@ module.exports = class TransactionStateManger extends EventEmitter {
|
||||
this._setTxStatus(txId, 'rejected')
|
||||
}
|
||||
|
||||
// should update the status of the tx to 'unapproved'.
|
||||
setTxStatusUnapproved (txId) {
|
||||
this._setTxStatus(txId, 'unapproved')
|
||||
}
|
||||
// should update the status of the tx to 'approved'.
|
||||
setTxStatusApproved (txId) {
|
||||
this._setTxStatus(txId, 'approved')
|
||||
@ -236,7 +240,7 @@ module.exports = class TransactionStateManger extends EventEmitter {
|
||||
txMeta.status = status
|
||||
this.emit(`${txMeta.id}:${status}`, txId)
|
||||
this.emit(`tx:status-update`, txId, status)
|
||||
if (status === 'submitted' || status === 'rejected') {
|
||||
if (['submitted', 'rejected', 'failed'].includes(status)) {
|
||||
this.emit(`${txMeta.id}:finished`, txMeta)
|
||||
}
|
||||
this.updateTx(txMeta, `txStateManager: setting status to ${status}`)
|
||||
|
@ -5,7 +5,6 @@ const Dnode = require('dnode')
|
||||
const ObservableStore = require('obs-store')
|
||||
const asStream = require('obs-store/lib/asStream')
|
||||
const AccountTracker = require('./lib/account-tracker')
|
||||
const EthQuery = require('eth-query')
|
||||
const RpcEngine = require('json-rpc-engine')
|
||||
const debounce = require('debounce')
|
||||
const createEngineStream = require('json-rpc-middleware-stream/engineStream')
|
||||
@ -23,6 +22,7 @@ const ShapeShiftController = require('./controllers/shapeshift')
|
||||
const AddressBookController = require('./controllers/address-book')
|
||||
const InfuraController = require('./controllers/infura')
|
||||
const BlacklistController = require('./controllers/blacklist')
|
||||
const RecentBlocksController = require('./controllers/recent-blocks')
|
||||
const MessageManager = require('./lib/message-manager')
|
||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||
const TypedMessageManager = require('./lib/typed-message-manager')
|
||||
@ -34,13 +34,15 @@ const accountImporter = require('./account-import-strategies')
|
||||
const getBuyEthUrl = require('./lib/buy-eth-url')
|
||||
const Mutex = require('await-semaphore').Mutex
|
||||
const version = require('../manifest.json').version
|
||||
const BN = require('ethereumjs-util').BN
|
||||
const GWEI_BN = new BN('1000000000')
|
||||
const percentile = require('percentile')
|
||||
|
||||
module.exports = class MetamaskController extends EventEmitter {
|
||||
|
||||
constructor (opts) {
|
||||
super()
|
||||
|
||||
|
||||
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
|
||||
|
||||
this.opts = opts
|
||||
@ -91,8 +93,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.provider = this.initializeProvider()
|
||||
this.blockTracker = this.provider._blockTracker
|
||||
|
||||
// eth data query tools
|
||||
this.ethQuery = new EthQuery(this.provider)
|
||||
this.recentBlocksController = new RecentBlocksController({
|
||||
blockTracker: this.blockTracker,
|
||||
provider: this.provider,
|
||||
})
|
||||
|
||||
// account tracker watches balances, nonces, and any code at their address.
|
||||
this.accountTracker = new AccountTracker({
|
||||
provider: this.provider,
|
||||
@ -133,7 +138,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
|
||||
provider: this.provider,
|
||||
blockTracker: this.blockTracker,
|
||||
ethQuery: this.ethQuery,
|
||||
getGasPrice: this.getGasPrice.bind(this),
|
||||
})
|
||||
this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts))
|
||||
|
||||
@ -196,25 +201,30 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.blacklistController.store.subscribe((state) => {
|
||||
this.store.updateState({ BlacklistController: state })
|
||||
})
|
||||
this.recentBlocksController.store.subscribe((state) => {
|
||||
this.store.updateState({ RecentBlocks: state })
|
||||
})
|
||||
this.infuraController.store.subscribe((state) => {
|
||||
this.store.updateState({ InfuraController: state })
|
||||
})
|
||||
|
||||
// manual mem state subscriptions
|
||||
this.networkController.store.subscribe(this.sendUpdate.bind(this))
|
||||
this.accountTracker.store.subscribe(this.sendUpdate.bind(this))
|
||||
this.txController.memStore.subscribe(this.sendUpdate.bind(this))
|
||||
this.balancesController.store.subscribe(this.sendUpdate.bind(this))
|
||||
this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
||||
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
||||
this.typedMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
||||
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
|
||||
this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
|
||||
this.addressBookController.store.subscribe(this.sendUpdate.bind(this))
|
||||
this.currencyController.store.subscribe(this.sendUpdate.bind(this))
|
||||
this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
|
||||
this.shapeshiftController.store.subscribe(this.sendUpdate.bind(this))
|
||||
this.infuraController.store.subscribe(this.sendUpdate.bind(this))
|
||||
const sendUpdate = this.sendUpdate.bind(this)
|
||||
this.networkController.store.subscribe(sendUpdate)
|
||||
this.accountTracker.store.subscribe(sendUpdate)
|
||||
this.txController.memStore.subscribe(sendUpdate)
|
||||
this.balancesController.store.subscribe(sendUpdate)
|
||||
this.messageManager.memStore.subscribe(sendUpdate)
|
||||
this.personalMessageManager.memStore.subscribe(sendUpdate)
|
||||
this.typedMessageManager.memStore.subscribe(sendUpdate)
|
||||
this.keyringController.memStore.subscribe(sendUpdate)
|
||||
this.preferencesController.store.subscribe(sendUpdate)
|
||||
this.recentBlocksController.store.subscribe(sendUpdate)
|
||||
this.addressBookController.store.subscribe(sendUpdate)
|
||||
this.currencyController.store.subscribe(sendUpdate)
|
||||
this.noticeController.memStore.subscribe(sendUpdate)
|
||||
this.shapeshiftController.store.subscribe(sendUpdate)
|
||||
this.infuraController.store.subscribe(sendUpdate)
|
||||
}
|
||||
|
||||
//
|
||||
@ -298,6 +308,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.currencyController.store.getState(),
|
||||
this.noticeController.memStore.getState(),
|
||||
this.infuraController.store.getState(),
|
||||
this.recentBlocksController.store.getState(),
|
||||
// config manager
|
||||
this.configManager.getConfig(),
|
||||
this.shapeshiftController.store.getState(),
|
||||
@ -342,6 +353,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
submitPassword: nodeify(keyringController.submitPassword, keyringController),
|
||||
|
||||
// network management
|
||||
setNetworkEndpoints: nodeify(networkController.setNetworkEndpoints, networkController),
|
||||
setProviderType: nodeify(networkController.setProviderType, networkController),
|
||||
setCustomRpc: nodeify(this.setCustomRpc, this),
|
||||
|
||||
@ -365,7 +377,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
|
||||
// txController
|
||||
cancelTransaction: nodeify(txController.cancelTransaction, txController),
|
||||
updateTransaction: nodeify(txController.updateTransaction, txController),
|
||||
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
|
||||
retryTransaction: nodeify(this.retryTransaction, this),
|
||||
|
||||
// messageManager
|
||||
signMessage: nodeify(this.signMessage, this),
|
||||
@ -475,6 +489,33 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.emit('update', this.getState())
|
||||
}
|
||||
|
||||
getGasPrice () {
|
||||
const { recentBlocksController } = this
|
||||
const { recentBlocks } = recentBlocksController.store.getState()
|
||||
|
||||
// Return 1 gwei if no blocks have been observed:
|
||||
if (recentBlocks.length === 0) {
|
||||
return '0x' + GWEI_BN.toString(16)
|
||||
}
|
||||
|
||||
const lowestPrices = recentBlocks.map((block) => {
|
||||
if (!block.gasPrices || block.gasPrices.length < 1) {
|
||||
return GWEI_BN
|
||||
}
|
||||
return block.gasPrices
|
||||
.map(hexPrefix => hexPrefix.substr(2))
|
||||
.map(hex => new BN(hex, 16))
|
||||
.sort((a, b) => {
|
||||
return a.gt(b) ? 1 : -1
|
||||
})[0]
|
||||
})
|
||||
.map(number => number.div(GWEI_BN).toNumber())
|
||||
|
||||
const percentileNum = percentile(50, lowestPrices)
|
||||
const percentileNumBn = new BN(percentileNum)
|
||||
return '0x' + percentileNumBn.mul(GWEI_BN).toString(16)
|
||||
}
|
||||
|
||||
//
|
||||
// Vault Management
|
||||
//
|
||||
@ -504,10 +545,15 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
|
||||
async createNewVaultAndRestore (password, seed) {
|
||||
const release = await this.createVaultMutex.acquire()
|
||||
try {
|
||||
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
|
||||
this.selectFirstIdentity(vault)
|
||||
release()
|
||||
return vault
|
||||
} catch (err) {
|
||||
release()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
selectFirstIdentity (vault) {
|
||||
@ -576,6 +622,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
//
|
||||
// Identity Management
|
||||
//
|
||||
//
|
||||
|
||||
async retryTransaction (txId, cb) {
|
||||
await this.txController.retryTransaction(txId)
|
||||
const state = await this.getState()
|
||||
return state
|
||||
}
|
||||
|
||||
|
||||
newUnsignedMessage (msgParams, cb) {
|
||||
const msgId = this.messageManager.addUnapprovedMessage(msgParams)
|
||||
|
@ -77,7 +77,7 @@ module.exports = class NoticeController extends EventEmitter {
|
||||
return uniqBy(oldNotices.concat(newNotices), 'id')
|
||||
}
|
||||
|
||||
_filterNotices(notices) {
|
||||
_filterNotices (notices) {
|
||||
return notices.filter((newNotice) => {
|
||||
if ('version' in newNotice) {
|
||||
const satisfied = semver.satisfies(this.version, newNotice.version)
|
||||
|
@ -26,8 +26,17 @@ const container = document.getElementById('app-content')
|
||||
startPopup({ container, connectionStream }, (err, store) => {
|
||||
if (err) return displayCriticalError(err)
|
||||
|
||||
let betaUIState = store.getState().metamask.featureFlags.betaUI
|
||||
let css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
|
||||
// Code commented out until we begin auto adding users to NewUI
|
||||
// const { isMascara, identities = {}, featureFlags = {} } = store.getState().metamask
|
||||
// const firstTime = Object.keys(identities).length === 0
|
||||
const { isMascara, featureFlags = {} } = store.getState().metamask
|
||||
let betaUIState = featureFlags.betaUI
|
||||
|
||||
// Code commented out until we begin auto adding users to NewUI
|
||||
// const useBetaCss = isMascara || firstTime || betaUIState
|
||||
const useBetaCss = isMascara || betaUIState
|
||||
|
||||
let css = useBetaCss ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
|
||||
let deleteInjectedCss = injectCss(css)
|
||||
let newBetaUIState
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
"frequentRpcList": [],
|
||||
"unapprovedTxs": {},
|
||||
"currentCurrency": "USD",
|
||||
"featureFlags": {"betaUI": true},
|
||||
"conversionRate": 12.7527416,
|
||||
"conversionDate": 1487624341,
|
||||
"noActiveNotices": false,
|
||||
|
739
development/states/pending-tx.json
Normal file
739
development/states/pending-tx.json
Normal file
@ -0,0 +1,739 @@
|
||||
{
|
||||
"metamask": {
|
||||
"isInitialized": true,
|
||||
"isUnlocked": true,
|
||||
"isMascara": false,
|
||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||
"identities": {
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"name": "Account 1"
|
||||
}
|
||||
},
|
||||
"unapprovedTxs": {},
|
||||
"noActiveNotices": true,
|
||||
"frequentRpcList": [
|
||||
"http://192.168.1.34:7545/"
|
||||
],
|
||||
"addressBook": [],
|
||||
"tokenExchangeRates": {},
|
||||
"coinOptions": {},
|
||||
"provider": {
|
||||
"type": "mainnet",
|
||||
"rpcTarget": "https://mainnet.infura.io/metamask"
|
||||
},
|
||||
"network": "1",
|
||||
"accounts": {
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||
"code": "0x",
|
||||
"balance": "0x1b3f641ed0c2f62",
|
||||
"nonce": "0x35",
|
||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||
}
|
||||
},
|
||||
"currentBlockGasLimit": "0x66df83",
|
||||
"selectedAddressTxList": [
|
||||
{
|
||||
"id": 3516145537630216,
|
||||
"time": 1512615655535,
|
||||
"status": "submitted",
|
||||
"metamaskNetworkId": "1",
|
||||
"txParams": {
|
||||
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"value": "0x16345785d8a0000",
|
||||
"gasPrice": "0xc1b710800",
|
||||
"gas": "0x7b0c",
|
||||
"nonce": "0x35",
|
||||
"chainId": "0x1"
|
||||
},
|
||||
"gasPriceSpecified": false,
|
||||
"gasLimitSpecified": false,
|
||||
"estimatedGas": "5208",
|
||||
"history": [
|
||||
{
|
||||
"id": 3516145537630216,
|
||||
"time": 1512615655535,
|
||||
"status": "unapproved",
|
||||
"metamaskNetworkId": "1",
|
||||
"txParams": {
|
||||
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"value": "0x16345785d8a0000",
|
||||
"gasPrice": "0xe6f7cec00",
|
||||
"gas": "0x7b0c"
|
||||
},
|
||||
"gasPriceSpecified": false,
|
||||
"gasLimitSpecified": false,
|
||||
"estimatedGas": "5208"
|
||||
},
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/txParams/gasPrice",
|
||||
"value": "0xc1b710800",
|
||||
"note": "confTx: user approved transaction"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "approved",
|
||||
"note": "txStateManager: setting status to approved"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/txParams/nonce",
|
||||
"value": "0x35",
|
||||
"note": "transactions#approveTransaction"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/nonceDetails",
|
||||
"value": {
|
||||
"params": {
|
||||
"highestLocalNonce": 53,
|
||||
"highestSuggested": 53,
|
||||
"nextNetworkNonce": 53
|
||||
},
|
||||
"local": {
|
||||
"name": "local",
|
||||
"nonce": 53,
|
||||
"details": {
|
||||
"startPoint": 53,
|
||||
"highest": 53
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"name": "network",
|
||||
"nonce": 53,
|
||||
"details": {
|
||||
"baseCount": 53
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/txParams/chainId",
|
||||
"value": "0x1",
|
||||
"note": "txStateManager: setting status to signed"
|
||||
},
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "signed"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/rawTx",
|
||||
"value": "0xf86c35850c1b710800827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0f5142ba79a13ca7ec65548953017edafb217803244bbf9821d9ad077d89921e9a03afcb614169c90be9905d5b469d06984825c76675d3a535937cdb8f2ad1c0a95",
|
||||
"note": "transactions#publishTransaction"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/hash",
|
||||
"value": "0x7ce19c0d128ca11293b44a4e6d3cc9063665c00ea8c8eb400f548e132c147353",
|
||||
"note": "transactions#setTxHash"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "submitted",
|
||||
"note": "txStateManager: setting status to submitted"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/firstRetryBlockNumber",
|
||||
"value": "0x478ab3",
|
||||
"note": "transactions/pending-tx-tracker#event: tx:block-update"
|
||||
}
|
||||
]
|
||||
],
|
||||
"nonceDetails": {
|
||||
"params": {
|
||||
"highestLocalNonce": 53,
|
||||
"highestSuggested": 53,
|
||||
"nextNetworkNonce": 53
|
||||
},
|
||||
"local": {
|
||||
"name": "local",
|
||||
"nonce": 53,
|
||||
"details": {
|
||||
"startPoint": 53,
|
||||
"highest": 53
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"name": "network",
|
||||
"nonce": 53,
|
||||
"details": {
|
||||
"baseCount": 53
|
||||
}
|
||||
}
|
||||
},
|
||||
"rawTx": "0xf86c35850c1b710800827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0f5142ba79a13ca7ec65548953017edafb217803244bbf9821d9ad077d89921e9a03afcb614169c90be9905d5b469d06984825c76675d3a535937cdb8f2ad1c0a95",
|
||||
"hash": "0x7ce19c0d128ca11293b44a4e6d3cc9063665c00ea8c8eb400f548e132c147353",
|
||||
"firstRetryBlockNumber": "0x478ab3"
|
||||
},
|
||||
{
|
||||
"id": 3516145537630211,
|
||||
"time": 1512613432658,
|
||||
"status": "confirmed",
|
||||
"metamaskNetworkId": "1",
|
||||
"txParams": {
|
||||
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"value": "0x16345785d8a0000",
|
||||
"gasPrice": "0xba43b7400",
|
||||
"gas": "0x7b0c",
|
||||
"nonce": "0x34",
|
||||
"chainId": "0x1"
|
||||
},
|
||||
"gasPriceSpecified": false,
|
||||
"gasLimitSpecified": false,
|
||||
"estimatedGas": "5208",
|
||||
"history": [
|
||||
{
|
||||
"id": 3516145537630211,
|
||||
"time": 1512613432658,
|
||||
"status": "unapproved",
|
||||
"metamaskNetworkId": "1",
|
||||
"txParams": {
|
||||
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"value": "0x16345785d8a0000",
|
||||
"gasPrice": "0xdf8475800",
|
||||
"gas": "0x7b0c"
|
||||
},
|
||||
"gasPriceSpecified": false,
|
||||
"gasLimitSpecified": false,
|
||||
"estimatedGas": "5208"
|
||||
},
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/txParams/gasPrice",
|
||||
"value": "0xba43b7400",
|
||||
"note": "confTx: user approved transaction"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "approved",
|
||||
"note": "txStateManager: setting status to approved"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/txParams/nonce",
|
||||
"value": "0x34",
|
||||
"note": "transactions#approveTransaction"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/nonceDetails",
|
||||
"value": {
|
||||
"params": {
|
||||
"highestLocalNonce": 52,
|
||||
"highestSuggested": 52,
|
||||
"nextNetworkNonce": 52
|
||||
},
|
||||
"local": {
|
||||
"name": "local",
|
||||
"nonce": 52,
|
||||
"details": {
|
||||
"startPoint": 52,
|
||||
"highest": 52
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"name": "network",
|
||||
"nonce": 52,
|
||||
"details": {
|
||||
"baseCount": 52
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/txParams/chainId",
|
||||
"value": "0x1",
|
||||
"note": "txStateManager: setting status to signed"
|
||||
},
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "signed"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/rawTx",
|
||||
"value": "0xf86c34850ba43b7400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a073a4afdb8e8ad32b0cf9039af56c66baffd60d30e75cee5c1b783208824eafb8a0021ca6c1714a2c71281333ab77f776d3514348ab77967280fca8a5b4be44285e",
|
||||
"note": "transactions#publishTransaction"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/hash",
|
||||
"value": "0x5c98409883fdfd3cd24058a83b91470da6c40ffae41a40eb90d7dee0b837d26d",
|
||||
"note": "transactions#setTxHash"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "submitted",
|
||||
"note": "txStateManager: setting status to submitted"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/firstRetryBlockNumber",
|
||||
"value": "0x478a2c",
|
||||
"note": "transactions/pending-tx-tracker#event: tx:block-update"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "confirmed",
|
||||
"note": "txStateManager: setting status to confirmed"
|
||||
}
|
||||
]
|
||||
],
|
||||
"nonceDetails": {
|
||||
"params": {
|
||||
"highestLocalNonce": 52,
|
||||
"highestSuggested": 52,
|
||||
"nextNetworkNonce": 52
|
||||
},
|
||||
"local": {
|
||||
"name": "local",
|
||||
"nonce": 52,
|
||||
"details": {
|
||||
"startPoint": 52,
|
||||
"highest": 52
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"name": "network",
|
||||
"nonce": 52,
|
||||
"details": {
|
||||
"baseCount": 52
|
||||
}
|
||||
}
|
||||
},
|
||||
"rawTx": "0xf86c34850ba43b7400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a073a4afdb8e8ad32b0cf9039af56c66baffd60d30e75cee5c1b783208824eafb8a0021ca6c1714a2c71281333ab77f776d3514348ab77967280fca8a5b4be44285e",
|
||||
"hash": "0x5c98409883fdfd3cd24058a83b91470da6c40ffae41a40eb90d7dee0b837d26d",
|
||||
"firstRetryBlockNumber": "0x478a2c"
|
||||
},
|
||||
{
|
||||
"id": 3516145537630210,
|
||||
"time": 1512612826136,
|
||||
"status": "confirmed",
|
||||
"metamaskNetworkId": "1",
|
||||
"txParams": {
|
||||
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"value": "0x16345785d8a0000",
|
||||
"gasPrice": "0xa7a358200",
|
||||
"gas": "0x7b0c",
|
||||
"nonce": "0x33",
|
||||
"chainId": "0x1"
|
||||
},
|
||||
"gasPriceSpecified": false,
|
||||
"gasLimitSpecified": false,
|
||||
"estimatedGas": "5208",
|
||||
"history": [
|
||||
{
|
||||
"id": 3516145537630210,
|
||||
"time": 1512612826136,
|
||||
"status": "unapproved",
|
||||
"metamaskNetworkId": "1",
|
||||
"txParams": {
|
||||
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"value": "0x16345785d8a0000",
|
||||
"gasPrice": "0xba43b7400",
|
||||
"gas": "0x7b0c"
|
||||
},
|
||||
"gasPriceSpecified": false,
|
||||
"gasLimitSpecified": false,
|
||||
"estimatedGas": "5208"
|
||||
},
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/txParams/gasPrice",
|
||||
"value": "0xa7a358200",
|
||||
"note": "confTx: user approved transaction"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "approved",
|
||||
"note": "txStateManager: setting status to approved"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/txParams/nonce",
|
||||
"value": "0x33",
|
||||
"note": "transactions#approveTransaction"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/nonceDetails",
|
||||
"value": {
|
||||
"params": {
|
||||
"highestLocalNonce": 0,
|
||||
"highestSuggested": 51,
|
||||
"nextNetworkNonce": 51
|
||||
},
|
||||
"local": {
|
||||
"name": "local",
|
||||
"nonce": 51,
|
||||
"details": {
|
||||
"startPoint": 51,
|
||||
"highest": 51
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"name": "network",
|
||||
"nonce": 51,
|
||||
"details": {
|
||||
"baseCount": 51
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/txParams/chainId",
|
||||
"value": "0x1",
|
||||
"note": "txStateManager: setting status to signed"
|
||||
},
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "signed"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/rawTx",
|
||||
"value": "0xf86c33850a7a358200827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0021a8cd6c10208cc593e22af53637e5d127cee5cc6f9443a3e758a02afff1d7ca025f7420e974d3f2c668c165040987c72543a8e709bfea3528a62836a6ced9ce8",
|
||||
"note": "transactions#publishTransaction"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/hash",
|
||||
"value": "0x289772800898bc9cd414530d8581c0da257a9055e4aaaa6d10d92d700bfbd044",
|
||||
"note": "transactions#setTxHash"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "submitted",
|
||||
"note": "txStateManager: setting status to submitted"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/firstRetryBlockNumber",
|
||||
"value": "0x478a04",
|
||||
"note": "transactions/pending-tx-tracker#event: tx:block-update"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "confirmed",
|
||||
"note": "txStateManager: setting status to confirmed"
|
||||
}
|
||||
]
|
||||
],
|
||||
"nonceDetails": {
|
||||
"params": {
|
||||
"highestLocalNonce": 0,
|
||||
"highestSuggested": 51,
|
||||
"nextNetworkNonce": 51
|
||||
},
|
||||
"local": {
|
||||
"name": "local",
|
||||
"nonce": 51,
|
||||
"details": {
|
||||
"startPoint": 51,
|
||||
"highest": 51
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"name": "network",
|
||||
"nonce": 51,
|
||||
"details": {
|
||||
"baseCount": 51
|
||||
}
|
||||
}
|
||||
},
|
||||
"rawTx": "0xf86c33850a7a358200827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0021a8cd6c10208cc593e22af53637e5d127cee5cc6f9443a3e758a02afff1d7ca025f7420e974d3f2c668c165040987c72543a8e709bfea3528a62836a6ced9ce8",
|
||||
"hash": "0x289772800898bc9cd414530d8581c0da257a9055e4aaaa6d10d92d700bfbd044",
|
||||
"firstRetryBlockNumber": "0x478a04"
|
||||
},
|
||||
{
|
||||
"id": 3516145537630209,
|
||||
"time": 1512612809252,
|
||||
"status": "failed",
|
||||
"metamaskNetworkId": "1",
|
||||
"txParams": {
|
||||
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"value": "0x16345785d8a0000",
|
||||
"gasPrice": "0x77359400",
|
||||
"gas": "0x7b0c",
|
||||
"nonce": "0x33",
|
||||
"chainId": "0x1"
|
||||
},
|
||||
"gasPriceSpecified": false,
|
||||
"gasLimitSpecified": false,
|
||||
"estimatedGas": "5208",
|
||||
"history": [
|
||||
{
|
||||
"id": 3516145537630209,
|
||||
"time": 1512612809252,
|
||||
"status": "unapproved",
|
||||
"metamaskNetworkId": "1",
|
||||
"txParams": {
|
||||
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"value": "0x16345785d8a0000",
|
||||
"gasPrice": "0xba43b7400",
|
||||
"gas": "0x7b0c"
|
||||
},
|
||||
"gasPriceSpecified": false,
|
||||
"gasLimitSpecified": false,
|
||||
"estimatedGas": "5208"
|
||||
},
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/txParams/gasPrice",
|
||||
"value": "0x77359400",
|
||||
"note": "confTx: user approved transaction"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "approved",
|
||||
"note": "txStateManager: setting status to approved"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/txParams/nonce",
|
||||
"value": "0x33",
|
||||
"note": "transactions#approveTransaction"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/nonceDetails",
|
||||
"value": {
|
||||
"params": {
|
||||
"highestLocalNonce": 0,
|
||||
"highestSuggested": 51,
|
||||
"nextNetworkNonce": 51
|
||||
},
|
||||
"local": {
|
||||
"name": "local",
|
||||
"nonce": 51,
|
||||
"details": {
|
||||
"startPoint": 51,
|
||||
"highest": 51
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"name": "network",
|
||||
"nonce": 51,
|
||||
"details": {
|
||||
"baseCount": 51
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/txParams/chainId",
|
||||
"value": "0x1",
|
||||
"note": "txStateManager: setting status to signed"
|
||||
},
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "signed"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/rawTx",
|
||||
"value": "0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7",
|
||||
"note": "transactions#publishTransaction"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/err",
|
||||
"value": {
|
||||
"message": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced",
|
||||
"stack": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:60327:26\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88030:9\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16678:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16522:25)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16694:16\n at resultObj.id (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88012:9)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16813:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16527:17)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/status",
|
||||
"value": "failed",
|
||||
"note": "txStateManager: setting status to failed"
|
||||
}
|
||||
]
|
||||
],
|
||||
"nonceDetails": {
|
||||
"params": {
|
||||
"highestLocalNonce": 0,
|
||||
"highestSuggested": 51,
|
||||
"nextNetworkNonce": 51
|
||||
},
|
||||
"local": {
|
||||
"name": "local",
|
||||
"nonce": 51,
|
||||
"details": {
|
||||
"startPoint": 51,
|
||||
"highest": 51
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"name": "network",
|
||||
"nonce": 51,
|
||||
"details": {
|
||||
"baseCount": 51
|
||||
}
|
||||
}
|
||||
},
|
||||
"rawTx": "0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7",
|
||||
"err": {
|
||||
"message": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced",
|
||||
"stack": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:60327:26\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88030:9\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16678:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16522:25)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16694:16\n at resultObj.id (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88012:9)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16813:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16527:17)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"unapprovedMsgs": {},
|
||||
"unapprovedMsgCount": 0,
|
||||
"unapprovedPersonalMsgs": {},
|
||||
"unapprovedPersonalMsgCount": 0,
|
||||
"unapprovedTypedMessages": {},
|
||||
"unapprovedTypedMessagesCount": 0,
|
||||
"keyringTypes": [
|
||||
"Simple Key Pair",
|
||||
"HD Key Tree"
|
||||
],
|
||||
"keyrings": [
|
||||
{
|
||||
"type": "HD Key Tree",
|
||||
"accounts": [
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||
]
|
||||
}
|
||||
],
|
||||
"computedBalances": {},
|
||||
"currentAccountTab": "history",
|
||||
"tokens": [
|
||||
{
|
||||
"address": "0x0d8775f648430679a709e98d2b0cb6250d2887ef",
|
||||
"symbol": "BAT",
|
||||
"decimals": "18"
|
||||
}
|
||||
],
|
||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"currentCurrency": "usd",
|
||||
"conversionRate": 418.62,
|
||||
"conversionDate": 1512615622,
|
||||
"infuraNetworkStatus": {
|
||||
"mainnet": "ok",
|
||||
"ropsten": "ok",
|
||||
"kovan": "ok",
|
||||
"rinkeby": "ok"
|
||||
},
|
||||
"shapeShiftTxList": [],
|
||||
"lostAccounts": []
|
||||
},
|
||||
"appState": {
|
||||
"shouldClose": true,
|
||||
"menuOpen": false,
|
||||
"currentView": {
|
||||
"name": "accountDetail",
|
||||
"context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||
},
|
||||
"accountDetail": {
|
||||
"subview": "transactions",
|
||||
"accountExport": "none",
|
||||
"privateKey": ""
|
||||
},
|
||||
"transForward": false,
|
||||
"isLoading": false,
|
||||
"warning": null,
|
||||
"forgottenPassword": false,
|
||||
"scrollToBottom": false
|
||||
},
|
||||
"identities": {},
|
||||
"version": "3.12.1",
|
||||
"platform": {
|
||||
"arch": "x86-64",
|
||||
"nacl_arch": "x86-64",
|
||||
"os": "mac"
|
||||
},
|
||||
"browser": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
|
||||
}
|
@ -4,8 +4,8 @@ metamascara:
|
||||
ports:
|
||||
- "9001"
|
||||
environment:
|
||||
MASCARA_ORIGIN: "https://zero.metamask.io"
|
||||
MASCARA_ORIGIN: "https://wallet.metamask.io"
|
||||
VIRTUAL_PORT: "9001"
|
||||
VIRTUAL_HOST: "zero.metamask.io"
|
||||
LETSENCRYPT_HOST: "zero.metamask.io"
|
||||
VIRTUAL_HOST: "wallet.metamask.io"
|
||||
LETSENCRYPT_HOST: "wallet.metamask.io"
|
||||
LETSENCRYPT_EMAIL: "admin@metamask.io"
|
43
gulpfile.js
43
gulpfile.js
@ -19,10 +19,14 @@ var manifest = require('./app/manifest.json')
|
||||
var gulpif = require('gulp-if')
|
||||
var replace = require('gulp-replace')
|
||||
var mkdirp = require('mkdirp')
|
||||
var asyncEach = require('async/each')
|
||||
var exec = require('child_process').exec
|
||||
var sass = require('gulp-sass')
|
||||
var autoprefixer = require('gulp-autoprefixer')
|
||||
var gulpStylelint = require('gulp-stylelint')
|
||||
var stylefmt = require('gulp-stylefmt')
|
||||
var uglify = require('gulp-uglify-es').default
|
||||
var babel = require('gulp-babel')
|
||||
|
||||
|
||||
var disableDebugTools = gutil.env.disableDebugTools
|
||||
@ -159,6 +163,18 @@ gulp.task('copy:watch', function(){
|
||||
gulp.watch(['./app/{_locales,images}/*', './app/scripts/chromereload.js', './app/*.{html,json}'], gulp.series('copy'))
|
||||
})
|
||||
|
||||
// record deps
|
||||
|
||||
gulp.task('deps', function (cb) {
|
||||
exec('npm ls', (err, stdoutOutput, stderrOutput) => {
|
||||
if (err) return cb(err)
|
||||
const browsers = ['firefox','chrome','edge','opera']
|
||||
asyncEach(browsers, (target, done) => {
|
||||
fs.writeFile(`./dist/${target}/deps.txt`, stdoutOutput, done)
|
||||
}, cb)
|
||||
})
|
||||
})
|
||||
|
||||
// lint js
|
||||
|
||||
gulp.task('lint', function () {
|
||||
@ -232,8 +248,18 @@ var jsDevStrings = jsFiles.map(jsFile => `dev:js:${jsFile}`)
|
||||
var jsBuildStrings = jsFiles.map(jsFile => `build:js:${jsFile}`)
|
||||
|
||||
jsFiles.forEach((jsFile) => {
|
||||
gulp.task(`dev:js:${jsFile}`, bundleTask({ watch: true, label: jsFile, filename: `${jsFile}.js` }))
|
||||
gulp.task(`build:js:${jsFile}`, bundleTask({ watch: false, label: jsFile, filename: `${jsFile}.js` }))
|
||||
gulp.task(`dev:js:${jsFile}`, bundleTask({
|
||||
watch: true,
|
||||
label: jsFile,
|
||||
filename: `${jsFile}.js`,
|
||||
isBuild: false
|
||||
}))
|
||||
gulp.task(`build:js:${jsFile}`, bundleTask({
|
||||
watch: false,
|
||||
label: jsFile,
|
||||
filename: `${jsFile}.js`,
|
||||
isBuild: true
|
||||
}))
|
||||
})
|
||||
|
||||
// inpage must be built before all other scripts:
|
||||
@ -267,12 +293,18 @@ gulp.task('zip:edge', zipTask('edge'))
|
||||
gulp.task('zip:opera', zipTask('opera'))
|
||||
gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip:opera'))
|
||||
|
||||
// set env var for production
|
||||
gulp.task('apply-prod-environment', function(done) {
|
||||
process.env.NODE_ENV = 'production'
|
||||
done()
|
||||
});
|
||||
|
||||
// high level tasks
|
||||
|
||||
gulp.task('dev', gulp.series('build:scss', 'dev:js', 'copy', gulp.parallel('watch:scss', 'copy:watch', 'dev:reload')))
|
||||
|
||||
gulp.task('build', gulp.series('clean', 'build:scss', gulp.parallel('build:js', 'copy')))
|
||||
gulp.task('dist', gulp.series('build', 'zip'))
|
||||
gulp.task('build', gulp.series('clean', 'build:scss', gulp.parallel('build:js', 'copy', 'deps')))
|
||||
gulp.task('dist', gulp.series('apply-prod-environment', 'build', 'zip'))
|
||||
|
||||
// task generators
|
||||
|
||||
@ -365,7 +397,6 @@ function bundleTask(opts) {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
// convert bundle stream to gulp vinyl stream
|
||||
.pipe(source(opts.filename))
|
||||
// inject variables into bundle
|
||||
@ -375,6 +406,8 @@ function bundleTask(opts) {
|
||||
// sourcemaps
|
||||
// loads map from browserify file
|
||||
.pipe(gulpif(debug, sourcemaps.init({ loadMaps: true })))
|
||||
// Minification
|
||||
.pipe(gulpif(opts.isBuild, uglify()))
|
||||
// writes .map file
|
||||
.pipe(gulpif(debug, sourcemaps.write('./')))
|
||||
// write completed bundles
|
||||
|
@ -2,6 +2,7 @@ const path = require('path')
|
||||
const express = require('express')
|
||||
const createBundle = require('./util').createBundle
|
||||
const serveBundle = require('./util').serveBundle
|
||||
const compression = require('compression')
|
||||
|
||||
module.exports = createMetamascaraServer
|
||||
|
||||
@ -16,6 +17,8 @@ function createMetamascaraServer () {
|
||||
|
||||
// serve bundles
|
||||
const server = express()
|
||||
server.use(compression())
|
||||
|
||||
// ui window
|
||||
serveBundle(server, '/ui.js', uiBundle)
|
||||
server.use(express.static(path.join(__dirname, '/../ui/'), { setHeaders: (res) => res.set('X-Frame-Options', 'DENY') }))
|
||||
|
@ -23,7 +23,9 @@ function createBundle (entryPoint) {
|
||||
cache: {},
|
||||
packageCache: {},
|
||||
plugin: [watchify],
|
||||
}).transform('babelify')
|
||||
})
|
||||
.transform('babelify')
|
||||
.transform('uglifyify', { global: true })
|
||||
|
||||
bundler.on('update', bundle)
|
||||
bundle()
|
||||
|
@ -5,6 +5,8 @@ import { createNewVaultAndKeychain } from '../../../../ui/app/actions'
|
||||
import LoadingScreen from './loading-screen'
|
||||
import Breadcrumbs from './breadcrumbs'
|
||||
import { DEFAULT_ROUTE, IMPORT_ACCOUNT_ROUTE } from '../../../../ui/app/routes'
|
||||
import EventEmitter from 'events'
|
||||
import Mascot from '../../../../ui/app/components/mascot'
|
||||
|
||||
class CreatePasswordScreen extends Component {
|
||||
static propTypes = {
|
||||
@ -20,6 +22,11 @@ class CreatePasswordScreen extends Component {
|
||||
confirmPassword: '',
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
this.animationEventEmitter = new EventEmitter()
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const { isInitialized, isUnlocked, history } = this.props
|
||||
if (isInitialized || isUnlocked) {
|
||||
@ -56,12 +63,25 @@ class CreatePasswordScreen extends Component {
|
||||
render () {
|
||||
const { isLoading } = this.props
|
||||
|
||||
return (
|
||||
<div className="first-time-flow">
|
||||
{
|
||||
isLoading
|
||||
return isLoading
|
||||
? <LoadingScreen loadingMessage="Creating your new account" />
|
||||
: (
|
||||
<div>
|
||||
<h2 className="alpha-warning">Warning This is Experimental software and is a Developer BETA </h2>
|
||||
<div className="first-view-main">
|
||||
<div className="mascara-info">
|
||||
<Mascot
|
||||
animationEventEmitter={this.animationEventEmitter}
|
||||
width="225"
|
||||
height="225"
|
||||
/>
|
||||
<div className="info">
|
||||
MetaMask is a secure identity vault for Ethereum.
|
||||
</div>
|
||||
<div className="info">
|
||||
It allows you to hold ether & tokens, and interact with decentralized applications.
|
||||
</div>
|
||||
</div>
|
||||
<div className="create-password">
|
||||
<div className="create-password__title">
|
||||
Create Password
|
||||
@ -109,8 +129,7 @@ class CreatePasswordScreen extends Component {
|
||||
{ */ }
|
||||
<Breadcrumbs total={3} currentIndex={0} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
.first-time-flow {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
@ -5,6 +6,36 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.alpha-warning {
|
||||
background: #f7861c;
|
||||
color: #fff;
|
||||
line-height: 2em;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.first-view-main {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.mascara-info {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
margin-top: 70px;
|
||||
margin-right: 10vw;
|
||||
width: 35vw;
|
||||
max-width: 550px;
|
||||
}
|
||||
|
||||
.mascara-info :first-child {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
.create-password,
|
||||
.unique-image,
|
||||
.tou,
|
||||
@ -540,11 +571,10 @@ button.backup-phrase__confirm-seed-option:hover {
|
||||
text-transform: uppercase;
|
||||
margin: 35px 0 14px;
|
||||
transition: 200ms ease-in-out;
|
||||
background: #f7861c;
|
||||
background-color: rgba(247, 134, 28, 0.9);
|
||||
}
|
||||
|
||||
button.first-time-flow__button[disabled] {
|
||||
background-color: rgba(247, 134, 28, 0.9);
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
|
@ -4,5 +4,3 @@ When you log in to MetaMask, your current account is visible to every new site y
|
||||
|
||||
For your privacy, for now, please sign out of MetaMask when you're done using a site.
|
||||
|
||||
Also, by default, you will be signed in to a test network. To use real Ether, you must connect to the main network manually in the top left network menu.
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -78,9 +78,10 @@ AccountDetailScreen.prototype.render = function () {
|
||||
address: selected,
|
||||
}),
|
||||
]),
|
||||
h('div.flex-column', {
|
||||
h('flex-column', {
|
||||
style: {
|
||||
lineHeight: '10px',
|
||||
marginLeft: '15px',
|
||||
width: '100%',
|
||||
},
|
||||
}, [
|
||||
@ -101,7 +102,7 @@ AccountDetailScreen.prototype.render = function () {
|
||||
{
|
||||
style: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
},
|
||||
},
|
||||
@ -131,6 +132,8 @@ AccountDetailScreen.prototype.render = function () {
|
||||
AccountDropdowns,
|
||||
{
|
||||
style: {
|
||||
marginRight: '8px',
|
||||
marginLeft: 'auto',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
selected,
|
||||
@ -144,6 +147,7 @@ AccountDetailScreen.prototype.render = function () {
|
||||
]),
|
||||
h('.flex-row', {
|
||||
style: {
|
||||
width: '15em',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'baseline',
|
||||
},
|
||||
@ -157,10 +161,10 @@ AccountDetailScreen.prototype.render = function () {
|
||||
textOverflow: 'ellipsis',
|
||||
paddingTop: '3px',
|
||||
width: '5em',
|
||||
height: '15px',
|
||||
fontSize: '13px',
|
||||
fontFamily: 'Montserrat Light',
|
||||
textRendering: 'geometricPrecision',
|
||||
marginTop: '15px',
|
||||
marginBottom: '15px',
|
||||
color: '#AEAEAE',
|
||||
},
|
||||
@ -188,7 +192,7 @@ AccountDetailScreen.prototype.render = function () {
|
||||
},
|
||||
}),
|
||||
|
||||
h('div', {}, [
|
||||
h('.flex-grow'),
|
||||
|
||||
h('button', {
|
||||
onClick: () => props.dispatch(actions.buyEthView(selected)),
|
||||
@ -199,12 +203,11 @@ AccountDetailScreen.prototype.render = function () {
|
||||
onClick: () => props.dispatch(actions.showSendPage()),
|
||||
style: {
|
||||
marginBottom: '20px',
|
||||
marginRight: '8px',
|
||||
},
|
||||
}, 'SEND'),
|
||||
|
||||
]),
|
||||
|
||||
]),
|
||||
]),
|
||||
|
||||
// subview (tx history, pk export confirm, buy eth warning)
|
||||
|
@ -34,6 +34,7 @@ const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
|
||||
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
||||
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
|
||||
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
|
||||
const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
|
||||
|
||||
module.exports = connect(mapStateToProps)(App)
|
||||
|
||||
@ -396,7 +397,7 @@ App.prototype.renderDropdown = function () {
|
||||
h(DropdownMenuItem, {
|
||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||
onClick: () => { this.props.dispatch(actions.lockMetamask()) },
|
||||
}, 'Lock'),
|
||||
}, 'Log Out'),
|
||||
|
||||
h(DropdownMenuItem, {
|
||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||
@ -405,7 +406,10 @@ App.prototype.renderDropdown = function () {
|
||||
|
||||
h(DropdownMenuItem, {
|
||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||
onClick: () => { this.props.dispatch(actions.setFeatureFlag('betaUI', true)) },
|
||||
onClick: () => {
|
||||
this.props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
||||
.then(() => this.props.dispatch(actions.setNetworkEndpoints(BETA_UI_NETWORK_TYPE)))
|
||||
},
|
||||
}, 'Try Beta!'),
|
||||
])
|
||||
}
|
||||
@ -466,11 +470,6 @@ App.prototype.renderPrimary = function () {
|
||||
})
|
||||
}
|
||||
|
||||
if (props.seedWords) {
|
||||
log.debug('rendering seed words')
|
||||
return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
|
||||
}
|
||||
|
||||
// show initialize screen
|
||||
if (!props.isInitialized || props.forgottenPassword) {
|
||||
// show current view
|
||||
@ -505,6 +504,12 @@ App.prototype.renderPrimary = function () {
|
||||
}
|
||||
}
|
||||
|
||||
// show seed words screen
|
||||
if (props.seedWords) {
|
||||
log.debug('rendering seed words')
|
||||
return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
|
||||
}
|
||||
|
||||
// show current view
|
||||
switch (props.currentView.name) {
|
||||
|
||||
|
@ -38,6 +38,16 @@ PendingTx.prototype.render = function () {
|
||||
const txMeta = this.gatherTxMeta()
|
||||
const txParams = txMeta.txParams || {}
|
||||
|
||||
// Allow retry txs
|
||||
const { lastGasPrice } = txMeta
|
||||
let forceGasMin
|
||||
if (lastGasPrice) {
|
||||
const stripped = ethUtil.stripHexPrefix(lastGasPrice)
|
||||
const lastGas = new BN(stripped, 16)
|
||||
const priceBump = lastGas.divn('10')
|
||||
forceGasMin = lastGas.add(priceBump)
|
||||
}
|
||||
|
||||
// Account Details
|
||||
const address = txParams.from || props.selectedAddress
|
||||
const identity = props.identities[address] || { address: address }
|
||||
@ -199,7 +209,7 @@ PendingTx.prototype.render = function () {
|
||||
precision: 9,
|
||||
scale: 9,
|
||||
suffix: 'GWEI',
|
||||
min: MIN_GAS_PRICE_BN,
|
||||
min: forceGasMin || MIN_GAS_PRICE_BN,
|
||||
style: {
|
||||
position: 'relative',
|
||||
top: '5px',
|
||||
|
@ -4,6 +4,7 @@ const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../ui/app/actions')
|
||||
const NetworkIndicator = require('./components/network')
|
||||
const LoadingIndicator = require('./components/loading')
|
||||
const txHelper = require('../lib/tx-helper')
|
||||
const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
|
||||
|
||||
@ -60,6 +61,11 @@ ConfirmTxScreen.prototype.render = function () {
|
||||
|
||||
h('.flex-column.flex-grow', [
|
||||
|
||||
h(LoadingIndicator, {
|
||||
isLoading: txData.loadingDefaults,
|
||||
loadingMessage: 'Estimating transaction cost…',
|
||||
}),
|
||||
|
||||
// subtitle and nav
|
||||
h('.section-title.flex-row.flex-center', [
|
||||
!isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
||||
|
@ -119,7 +119,7 @@ ConfigScreen.prototype.render = function () {
|
||||
if (err) {
|
||||
state.dispatch(actions.displayWarning('Error in retrieving state logs.'))
|
||||
} else {
|
||||
exportAsFile('MetaMask State Logs', result)
|
||||
exportAsFile('MetaMask State Logs.json', result)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -21,6 +21,7 @@ html, body {
|
||||
background: #F7F7F7;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html {
|
||||
@ -107,6 +108,10 @@ button:not([disabled]):active, input[type="submit"]:not([disabled]):active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.grow-on-hover:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
@ -436,12 +441,14 @@ input.large-input {
|
||||
.account-detail-section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
max-height: 465px;
|
||||
flex-direction: inherit;
|
||||
}
|
||||
|
||||
.name-label {
|
||||
.account-detail-section .name-label {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.grow-tenx {
|
||||
|
@ -247,10 +247,26 @@ SendTransactionScreen.prototype.onSubmit = function () {
|
||||
const recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '')
|
||||
const nickname = state.nickname || ' '
|
||||
const input = document.querySelector('input[name="amount"]').value
|
||||
const parts = input.split('')
|
||||
|
||||
let message
|
||||
|
||||
if (isNaN(input) || input === '') {
|
||||
message = 'Invalid ether value.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if (parts[1]) {
|
||||
var decimal = parts[1]
|
||||
if (decimal.length > 18) {
|
||||
message = 'Ether amount is too precise.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
}
|
||||
|
||||
const value = util.normalizeEthStringToWei(input)
|
||||
const txData = document.querySelector('input[name="txData"]').value
|
||||
const balance = this.props.balance
|
||||
let message
|
||||
|
||||
if (value.gt(balance)) {
|
||||
message = 'Insufficient funds.'
|
||||
|
27
package.json
27
package.json
@ -9,11 +9,11 @@
|
||||
"ui": "npm run test:flat:build:states && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
||||
"mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
||||
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
|
||||
"mascara": "METAMASK_DEBUG=true node ./mascara/example/server",
|
||||
"mascara": "gulp build && METAMASK_DEBUG=true node ./mascara/example/server",
|
||||
"dist": "npm run dist:clear && npm install && gulp dist",
|
||||
"dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
|
||||
"test": "npm run lint && npm run test:coverage && npm run test:integration",
|
||||
"test:unit": "METAMASK_ENV=test mocha --compilers js:babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
|
||||
"test:unit": "METAMASK_ENV=test mocha --exit --compilers js:babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
|
||||
"test:single": "METAMASK_ENV=test mocha --require test/helper.js",
|
||||
"test:integration": "gulp build:scss && npm run test:flat && npm run test:mascara",
|
||||
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
|
||||
@ -77,14 +77,14 @@
|
||||
"eslint-plugin-react": "^7.4.0",
|
||||
"eth-bin-to-ops": "^1.0.1",
|
||||
"eth-block-tracker": "^2.2.0",
|
||||
"eth-json-rpc-filters": "^1.2.5",
|
||||
"eth-json-rpc-infura": "^2.0.5",
|
||||
"eth-keyring-controller": "^2.1.4",
|
||||
"eth-contract-metadata": "^1.1.5",
|
||||
"eth-hd-keyring": "^1.2.1",
|
||||
"eth-json-rpc-filters": "^1.2.4",
|
||||
"eth-keyring-controller": "^2.1.2",
|
||||
"eth-phishing-detect": "^1.1.4",
|
||||
"eth-query": "^2.1.2",
|
||||
"eth-sig-util": "^1.4.0",
|
||||
"eth-simple-keyring": "^1.2.0",
|
||||
"eth-sig-util": "^1.4.2",
|
||||
"eth-token-tracker": "^1.1.4",
|
||||
"ethereumjs-abi": "^0.6.4",
|
||||
"ethereumjs-tx": "^1.3.0",
|
||||
@ -129,6 +129,7 @@
|
||||
"obj-multiplex": "^1.0.0",
|
||||
"obs-store": "^3.0.0",
|
||||
"once": "^1.3.3",
|
||||
"percentile": "^1.2.0",
|
||||
"ping-pong-stream": "^1.0.0",
|
||||
"pojo-migrator": "^2.1.0",
|
||||
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
||||
@ -162,15 +163,15 @@
|
||||
"request-promise": "^4.2.1",
|
||||
"sandwich-expando": "^1.1.3",
|
||||
"semaphore": "^1.0.5",
|
||||
"shallow-copy": "0.0.1",
|
||||
"semver": "^5.4.1",
|
||||
"shallow-copy": "0.0.1",
|
||||
"sw-stream": "^2.0.0",
|
||||
"textarea-caret": "^3.0.1",
|
||||
"through2": "^2.0.3",
|
||||
"valid-url": "^1.0.9",
|
||||
"vreme": "^3.0.2",
|
||||
"web3": "^0.20.1",
|
||||
"web3-provider-engine": "^13.3.2",
|
||||
"web3-provider-engine": "^13.5.0",
|
||||
"web3-stream-provider": "^3.0.1",
|
||||
"xtend": "^4.0.1"
|
||||
},
|
||||
@ -200,16 +201,20 @@
|
||||
"eth-json-rpc-middleware": "^1.2.7",
|
||||
"fs-promise": "^2.0.3",
|
||||
"gulp": "github:gulpjs/gulp#4.0",
|
||||
"gulp-if": "^2.0.1",
|
||||
"gulp-babel": "^7.0.0",
|
||||
"gulp-if": "^2.0.2",
|
||||
"gulp-json-editor": "^2.2.1",
|
||||
"gulp-livereload": "^3.8.1",
|
||||
"gulp-replace": "^0.6.1",
|
||||
"gulp-sourcemaps": "^2.6.0",
|
||||
"gulp-stylefmt": "^1.1.0",
|
||||
"gulp-stylelint": "^4.0.0",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"gulp-uglify-es": "^1.0.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-watch": "^4.3.5",
|
||||
"gulp-zip": "^4.0.0",
|
||||
"gulp-eslint": "^4.0.0",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jsdom": "^11.1.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
@ -239,8 +244,8 @@
|
||||
"tape": "^4.5.1",
|
||||
"testem": "^1.10.3",
|
||||
"uglifyify": "^4.0.2",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0",
|
||||
"watchify": "^3.9.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -54,6 +54,8 @@ module.exports = function(config) {
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: Infinity
|
||||
concurrency: 1,
|
||||
|
||||
nocache: true,
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,8 @@ module.exports = {
|
||||
createEngineForTestData,
|
||||
providerFromEngine,
|
||||
scaffoldMiddleware,
|
||||
createStubedProvider
|
||||
createEthJsQueryStub,
|
||||
createStubedProvider,
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +19,18 @@ function providerFromEngine (engine) {
|
||||
return provider
|
||||
}
|
||||
|
||||
function createEthJsQueryStub (stubProvider) {
|
||||
return new Proxy({}, {
|
||||
get: (obj, method) => {
|
||||
return (...params) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
stubProvider.sendAsync({ method: `eth_${method}`, params }, (err, ress) => resolve(ress.result))
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function createStubedProvider (resultStub) {
|
||||
const engine = createEngineForTestData()
|
||||
engine.push(scaffoldMiddleware(resultStub))
|
||||
|
@ -51,9 +51,8 @@ describe('tx confirmation screen', function () {
|
||||
|
||||
actions.cancelTx({value: firstTxId})((action) => {
|
||||
result = reducers(initialState, action)
|
||||
done()
|
||||
})
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
it('should transition to the account detail view', function () {
|
||||
|
@ -3,6 +3,8 @@ const sinon = require('sinon')
|
||||
const clone = require('clone')
|
||||
const MetaMaskController = require('../../app/scripts/metamask-controller')
|
||||
const firstTimeState = require('../../app/scripts/first-time-state')
|
||||
const BN = require('ethereumjs-util').BN
|
||||
const GWEI_BN = new BN('1000000000')
|
||||
|
||||
describe('MetaMaskController', function () {
|
||||
const noop = () => {}
|
||||
@ -39,17 +41,63 @@ describe('MetaMaskController', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
|
||||
sinon.spy(metamaskController.keyringController, 'createNewVaultAndRestore')
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
metamaskController.keyringController.createNewVaultAndKeychain.restore()
|
||||
metamaskController.keyringController.createNewVaultAndRestore.restore()
|
||||
})
|
||||
|
||||
describe('#getGasPrice', function () {
|
||||
it('gives the 50th percentile lowest accepted gas price from recentBlocksController', async function () {
|
||||
const realRecentBlocksController = metamaskController.recentBlocksController
|
||||
metamaskController.recentBlocksController = {
|
||||
store: {
|
||||
getState: () => {
|
||||
return {
|
||||
recentBlocks: [
|
||||
{ gasPrices: [ '0x3b9aca00', '0x174876e800'] },
|
||||
{ gasPrices: [ '0x3b9aca00', '0x174876e800'] },
|
||||
{ gasPrices: [ '0x174876e800', '0x174876e800' ]},
|
||||
{ gasPrices: [ '0x174876e800', '0x174876e800' ]},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const gasPrice = metamaskController.getGasPrice()
|
||||
assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price')
|
||||
|
||||
metamaskController.recentBlocksController = realRecentBlocksController
|
||||
})
|
||||
|
||||
it('gives the 1 gwei price if no blocks have been seen.', async function () {
|
||||
const realRecentBlocksController = metamaskController.recentBlocksController
|
||||
metamaskController.recentBlocksController = {
|
||||
store: {
|
||||
getState: () => {
|
||||
return {
|
||||
recentBlocks: []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const gasPrice = metamaskController.getGasPrice()
|
||||
assert.equal(gasPrice, '0x' + GWEI_BN.toString(16), 'defaults to 1 gwei')
|
||||
|
||||
metamaskController.recentBlocksController = realRecentBlocksController
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('#createNewVaultAndKeychain', function () {
|
||||
it('can only create new vault on keyringController once', async function () {
|
||||
|
||||
const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity')
|
||||
|
||||
|
||||
const password = 'a-fake-password'
|
||||
|
||||
const first = await metamaskController.createNewVaultAndKeychain(password)
|
||||
@ -60,6 +108,22 @@ describe('MetaMaskController', function () {
|
||||
selectStub.reset()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createNewVaultAndRestore', function () {
|
||||
it('should be able to call newVaultAndRestore despite a mistake.', async function () {
|
||||
// const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity')
|
||||
|
||||
const password = 'what-what-what'
|
||||
const wrongSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadiu'
|
||||
const rightSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
|
||||
const first = await metamaskController.createNewVaultAndRestore(password, wrongSeed)
|
||||
.catch((e) => {
|
||||
return
|
||||
})
|
||||
const second = await metamaskController.createNewVaultAndRestore(password, rightSeed)
|
||||
|
||||
assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -340,4 +340,63 @@ describe('PendingTransactionTracker', function () {
|
||||
assert.equal(pendingTxTracker.publishTransaction.callCount, 1, 'Should call publish transaction')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#_checkIfNonceIsTaken', function () {
|
||||
beforeEach ( function () {
|
||||
let confirmedTxList = [{
|
||||
id: 1,
|
||||
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
|
||||
status: 'confirmed',
|
||||
txParams: {
|
||||
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
nonce: '0x1',
|
||||
value: '0xfffff',
|
||||
},
|
||||
rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
|
||||
}, {
|
||||
id: 2,
|
||||
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
|
||||
status: 'confirmed',
|
||||
txParams: {
|
||||
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
nonce: '0x2',
|
||||
value: '0xfffff',
|
||||
},
|
||||
rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
|
||||
}]
|
||||
pendingTxTracker.getCompletedTransactions = (address) => {
|
||||
if (!address) throw new Error('unless behavior has changed #_checkIfNonceIsTaken needs a filtered list of transactions to see if the nonce is taken')
|
||||
return confirmedTxList
|
||||
}
|
||||
})
|
||||
|
||||
it('should return false if nonce has not been taken', function (done) {
|
||||
pendingTxTracker._checkIfNonceIsTaken({
|
||||
txParams: {
|
||||
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
nonce: '0x3',
|
||||
value: '0xfffff',
|
||||
},
|
||||
})
|
||||
.then((taken) => {
|
||||
assert.ok(!taken)
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
it('should return true if nonce has been taken', function (done) {
|
||||
pendingTxTracker._checkIfNonceIsTaken({
|
||||
txParams: {
|
||||
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
nonce: '0x2',
|
||||
value: '0xfffff',
|
||||
},
|
||||
}).then((taken) => {
|
||||
assert.ok(taken)
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
48
test/unit/preferences-controller-test.js
Normal file
48
test/unit/preferences-controller-test.js
Normal file
@ -0,0 +1,48 @@
|
||||
const assert = require('assert')
|
||||
const PreferencesController = require('../../app/scripts/controllers/preferences')
|
||||
|
||||
describe('preferences controller', function () {
|
||||
let preferencesController
|
||||
|
||||
before(() => {
|
||||
preferencesController = new PreferencesController()
|
||||
})
|
||||
|
||||
describe('addToken', function () {
|
||||
it('should add that token to its state', async function () {
|
||||
const address = '0xabcdef1234567'
|
||||
const symbol = 'ABBR'
|
||||
const decimals = 5
|
||||
|
||||
await preferencesController.addToken(address, symbol, decimals)
|
||||
|
||||
const tokens = preferencesController.getTokens()
|
||||
assert.equal(tokens.length, 1, 'one token added')
|
||||
|
||||
const added = tokens[0]
|
||||
assert.equal(added.address, address, 'set address correctly')
|
||||
assert.equal(added.symbol, symbol, 'set symbol correctly')
|
||||
assert.equal(added.decimals, decimals, 'set decimals correctly')
|
||||
})
|
||||
|
||||
it('should allow updating a token value', async function () {
|
||||
const address = '0xabcdef1234567'
|
||||
const symbol = 'ABBR'
|
||||
const decimals = 5
|
||||
|
||||
await preferencesController.addToken(address, symbol, decimals)
|
||||
|
||||
const newDecimals = 6
|
||||
await preferencesController.addToken(address, symbol, newDecimals)
|
||||
|
||||
const tokens = preferencesController.getTokens()
|
||||
assert.equal(tokens.length, 1, 'one token added')
|
||||
|
||||
const added = tokens[0]
|
||||
assert.equal(added.address, address, 'set address correctly')
|
||||
assert.equal(added.symbol, symbol, 'set symbol correctly')
|
||||
assert.equal(added.decimals, newDecimals, 'updated decimals correctly')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ const ObservableStore = require('obs-store')
|
||||
const sinon = require('sinon')
|
||||
const TransactionController = require('../../app/scripts/controllers/transactions')
|
||||
const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils')
|
||||
const { createStubedProvider } = require('../stub/provider')
|
||||
const { createStubedProvider, createEthJsQueryStub } = require('../stub/provider')
|
||||
|
||||
const noop = () => true
|
||||
const currentNetworkId = 42
|
||||
@ -30,6 +30,8 @@ describe('Transaction Controller', function () {
|
||||
resolve()
|
||||
}),
|
||||
})
|
||||
txController.query = createEthJsQueryStub(provider)
|
||||
txController.txGasUtil.query = createEthJsQueryStub(provider)
|
||||
txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop })
|
||||
txController.txProviderUtils = new TxGasUtils(txController.provider)
|
||||
})
|
||||
@ -110,22 +112,15 @@ describe('Transaction Controller', function () {
|
||||
history: [],
|
||||
}
|
||||
txController.txStateManager._saveTxList([txMeta])
|
||||
stub = sinon.stub(txController, 'addUnapprovedTransaction').returns(Promise.resolve(txController.txStateManager.addTx(txMeta)))
|
||||
stub = sinon.stub(txController, 'addUnapprovedTransaction').callsFake(() => {
|
||||
txController.emit('newUnapprovedTx', txMeta)
|
||||
return Promise.resolve(txController.txStateManager.addTx(txMeta))
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
txController.txStateManager._saveTxList([])
|
||||
stub.restore()
|
||||
})
|
||||
|
||||
it('should emit newUnapprovedTx event and pass txMeta as the first argument', function (done) {
|
||||
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
||||
assert(txMetaFromEmit, 'txMeta is falsey')
|
||||
assert.equal(txMetaFromEmit.id, 1, 'the right txMeta was passed')
|
||||
done()
|
||||
})
|
||||
txController.newUnapprovedTransaction(txParams)
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
it('should resolve when finished and status is submitted and resolve with the hash', function (done) {
|
||||
@ -160,8 +155,17 @@ describe('Transaction Controller', function () {
|
||||
})
|
||||
|
||||
describe('#addUnapprovedTransaction', function () {
|
||||
let addTxDefaults
|
||||
beforeEach(() => {
|
||||
addTxDefaults = txController.addTxDefaults
|
||||
txController.addTxDefaults = function addTxDefaultsStub () { return Promise.resolve() }
|
||||
|
||||
})
|
||||
afterEach(() => {
|
||||
txController.addTxDefaults = addTxDefaults
|
||||
})
|
||||
|
||||
it('should add an unapproved transaction and return a valid txMeta', function (done) {
|
||||
const addTxDefaultsStub = sinon.stub(txController, 'addTxDefaults').callsFake(() => Promise.resolve())
|
||||
txController.addUnapprovedTransaction({})
|
||||
.then((txMeta) => {
|
||||
assert(('id' in txMeta), 'should have a id')
|
||||
@ -172,10 +176,20 @@ describe('Transaction Controller', function () {
|
||||
|
||||
const memTxMeta = txController.txStateManager.getTx(txMeta.id)
|
||||
assert.deepEqual(txMeta, memTxMeta, `txMeta should be stored in txController after adding it\n expected: ${txMeta} \n got: ${memTxMeta}`)
|
||||
addTxDefaultsStub.restore()
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
|
||||
it('should emit newUnapprovedTx event and pass txMeta as the first argument', function (done) {
|
||||
providerResultStub.eth_gasPrice = '4a817c800'
|
||||
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
||||
assert(txMetaFromEmit, 'txMeta is falsey')
|
||||
done()
|
||||
})
|
||||
txController.addUnapprovedTransaction({})
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('#addTxDefaults', function () {
|
||||
|
32
test/unit/tx-gas-util-test.js
Normal file
32
test/unit/tx-gas-util-test.js
Normal file
@ -0,0 +1,32 @@
|
||||
const assert = require('assert')
|
||||
const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils')
|
||||
const { createStubedProvider } = require('../stub/provider')
|
||||
|
||||
describe('Tx Gas Util', function () {
|
||||
let txGasUtil, provider, providerResultStub
|
||||
beforeEach(function () {
|
||||
providerResultStub = {}
|
||||
provider = createStubedProvider(providerResultStub)
|
||||
txGasUtil = new TxGasUtils({
|
||||
provider,
|
||||
})
|
||||
})
|
||||
|
||||
it('removes recipient for txParams with 0x when contract data is provided', function () {
|
||||
const zeroRecipientandDataTxParams = {
|
||||
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
to: '0x',
|
||||
data: 'bytecode',
|
||||
}
|
||||
const sanitizedTxParams = txGasUtil.validateRecipient(zeroRecipientandDataTxParams)
|
||||
assert.deepEqual(sanitizedTxParams, { from: '0x1678a085c290ebd122dc42cba69373b5953b831d', data: 'bytecode' }, 'no recipient with 0x')
|
||||
})
|
||||
|
||||
it('should error when recipient is 0x', function () {
|
||||
const zeroRecipientTxParams = {
|
||||
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
to: '0x',
|
||||
}
|
||||
assert.throws(() => { txGasUtil.validateRecipient(zeroRecipientTxParams) }, Error, 'Invalid recipient address')
|
||||
})
|
||||
})
|
@ -201,6 +201,18 @@ describe('util', function () {
|
||||
var output = util.normalizeEthStringToWei(input)
|
||||
assert.equal(output.toString(10), ethInWei)
|
||||
})
|
||||
|
||||
it('should account for overflow numbers gracefully by dropping extra precision.', function () {
|
||||
var input = '1.11111111111111111111'
|
||||
var output = util.normalizeEthStringToWei(input)
|
||||
assert.equal(output.toString(10), '1111111111111111111')
|
||||
})
|
||||
|
||||
it('should not truncate very exact wei values that do not have extra precision.', function () {
|
||||
var input = '1.100000000000000001'
|
||||
var output = util.normalizeEthStringToWei(input)
|
||||
assert.equal(output.toString(10), '1100000000000000001')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#normalizeNumberToWei', function () {
|
||||
|
@ -2,7 +2,6 @@ const inherits = require('util').inherits
|
||||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../actions')
|
||||
import Select from 'react-select'
|
||||
|
||||
// Subviews
|
||||
@ -34,37 +33,14 @@ AccountImportSubview.prototype.render = function () {
|
||||
const { type } = state
|
||||
|
||||
return (
|
||||
h('div.flex-center', {
|
||||
style: {
|
||||
flexDirection: 'column',
|
||||
marginTop: '32px',
|
||||
},
|
||||
}, [
|
||||
h('.section-title.flex-row.flex-center', [
|
||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
||||
onClick: (event) => {
|
||||
props.dispatch(actions.goHome())
|
||||
},
|
||||
}),
|
||||
h('h2.page-subtitle', 'Import Accounts'),
|
||||
]),
|
||||
h('div', {
|
||||
style: {
|
||||
padding: '10px 0',
|
||||
width: '260px',
|
||||
color: 'rgb(174, 174, 174)',
|
||||
},
|
||||
}, [
|
||||
h('div.new-account-import-form', [
|
||||
|
||||
h('h3', { style: { padding: '3px' } }, 'SELECT TYPE'),
|
||||
h('div.new-account-import-form__select-section', [
|
||||
|
||||
h('style', `
|
||||
.has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label {
|
||||
color: rgb(174,174,174);
|
||||
}
|
||||
`),
|
||||
h('div.new-account-import-form__select-label', 'SELECT TYPE'),
|
||||
|
||||
h(Select, {
|
||||
className: 'new-account-import-form__select',
|
||||
name: 'import-type-select',
|
||||
clearable: false,
|
||||
value: type || menuItems[0],
|
||||
@ -75,10 +51,10 @@ AccountImportSubview.prototype.render = function () {
|
||||
}
|
||||
}),
|
||||
onChange: (opt) => {
|
||||
props.dispatch(actions.showImportPage())
|
||||
this.setState({ type: opt.value })
|
||||
},
|
||||
}),
|
||||
|
||||
]),
|
||||
|
||||
this.renderImportView(),
|
||||
|
96
ui/app/accounts/new-account/create-form.js
Normal file
96
ui/app/accounts/new-account/create-form.js
Normal file
@ -0,0 +1,96 @@
|
||||
const { Component } = require('react')
|
||||
const PropTypes = require('prop-types')
|
||||
const h = require('react-hyperscript')
|
||||
const { connect } = require('react-redux')
|
||||
const actions = require('../../actions')
|
||||
|
||||
class NewAccountCreateForm extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
const { numberOfExistingAccounts = 0 } = props
|
||||
const newAccountNumber = numberOfExistingAccounts + 1
|
||||
|
||||
this.state = {
|
||||
newAccountName: `Account ${newAccountNumber}`,
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { newAccountName } = this.state
|
||||
|
||||
return h('div.new-account-create-form', [
|
||||
|
||||
h('div.new-account-create-form__input-label', {}, [
|
||||
'Account Name',
|
||||
]),
|
||||
|
||||
h('div.new-account-create-form__input-wrapper', {}, [
|
||||
h('input.new-account-create-form__input', {
|
||||
value: this.state.newAccountName,
|
||||
placeholder: 'E.g. My new account',
|
||||
onChange: event => this.setState({ newAccountName: event.target.value }),
|
||||
}, []),
|
||||
]),
|
||||
|
||||
h('div.new-account-create-form__buttons', {}, [
|
||||
|
||||
h('button.new-account-create-form__button-cancel', {
|
||||
onClick: () => this.props.goHome(),
|
||||
}, [
|
||||
'CANCEL',
|
||||
]),
|
||||
|
||||
h('button.new-account-create-form__button-create', {
|
||||
onClick: () => this.props.createAccount(newAccountName),
|
||||
}, [
|
||||
'CREATE',
|
||||
]),
|
||||
|
||||
]),
|
||||
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
NewAccountCreateForm.propTypes = {
|
||||
hideModal: PropTypes.func,
|
||||
showImportPage: PropTypes.func,
|
||||
createAccount: PropTypes.func,
|
||||
goHome: PropTypes.func,
|
||||
numberOfExistingAccounts: PropTypes.number,
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { metamask: { network, selectedAddress, identities = {} } } = state
|
||||
const numberOfExistingAccounts = Object.keys(identities).length
|
||||
|
||||
return {
|
||||
network,
|
||||
address: selectedAddress,
|
||||
numberOfExistingAccounts,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
toCoinbase: (address) => {
|
||||
dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
|
||||
},
|
||||
hideModal: () => {
|
||||
dispatch(actions.hideModal())
|
||||
},
|
||||
createAccount: (newAccountName) => {
|
||||
dispatch(actions.addNewAccount())
|
||||
.then((newAccountAddress) => {
|
||||
if (newAccountName) {
|
||||
dispatch(actions.saveAccountLabel(newAccountAddress, newAccountName))
|
||||
}
|
||||
dispatch(actions.goHome())
|
||||
})
|
||||
},
|
||||
showImportPage: () => dispatch(actions.showImportPage()),
|
||||
goHome: () => dispatch(actions.goHome()),
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(NewAccountCreateForm)
|
81
ui/app/accounts/new-account/index.js
Normal file
81
ui/app/accounts/new-account/index.js
Normal file
@ -0,0 +1,81 @@
|
||||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../actions')
|
||||
const { getCurrentViewContext } = require('../../selectors')
|
||||
const classnames = require('classnames')
|
||||
|
||||
const NewAccountCreateForm = require('./create-form')
|
||||
const NewAccountImportForm = require('../import')
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
displayedForm: getCurrentViewContext(state),
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
displayForm: form => dispatch(actions.setNewAccountForm(form)),
|
||||
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
|
||||
showExportPrivateKeyModal: () => {
|
||||
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
|
||||
},
|
||||
hideModal: () => dispatch(actions.hideModal()),
|
||||
saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)),
|
||||
}
|
||||
}
|
||||
|
||||
inherits(AccountDetailsModal, Component)
|
||||
function AccountDetailsModal (props) {
|
||||
Component.call(this)
|
||||
|
||||
this.state = {
|
||||
displayedForm: props.displayedForm,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailsModal)
|
||||
|
||||
AccountDetailsModal.prototype.render = function () {
|
||||
const { displayedForm, displayForm } = this.props
|
||||
|
||||
return h('div.new-account', {}, [
|
||||
|
||||
h('div.new-account__header', [
|
||||
|
||||
h('div.new-account__title', 'New Account'),
|
||||
|
||||
h('div.new-account__tabs', [
|
||||
|
||||
h('div.new-account__tabs__tab', {
|
||||
className: classnames('new-account__tabs__tab', {
|
||||
'new-account__tabs__selected': displayedForm === 'CREATE',
|
||||
'new-account__tabs__unselected cursor-pointer': displayedForm !== 'CREATE',
|
||||
}),
|
||||
onClick: () => displayForm('CREATE'),
|
||||
}, 'Create'),
|
||||
|
||||
h('div.new-account__tabs__tab', {
|
||||
className: classnames('new-account__tabs__tab', {
|
||||
'new-account__tabs__selected': displayedForm === 'IMPORT',
|
||||
'new-account__tabs__unselected cursor-pointer': displayedForm !== 'IMPORT',
|
||||
}),
|
||||
onClick: () => displayForm('IMPORT'),
|
||||
}, 'Import'),
|
||||
|
||||
]),
|
||||
|
||||
]),
|
||||
|
||||
h('div.new-account__form', [
|
||||
|
||||
displayedForm === 'CREATE'
|
||||
? h(NewAccountCreateForm)
|
||||
: h(NewAccountImportForm),
|
||||
|
||||
]),
|
||||
|
||||
])
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
const abi = require('human-standard-token-abi')
|
||||
const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url')
|
||||
const { getTokenAddressFromTokenObject } = require('./util')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
|
||||
var actions = {
|
||||
@ -50,12 +51,16 @@ var actions = {
|
||||
SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED',
|
||||
SHOW_INFO_PAGE: 'SHOW_INFO_PAGE',
|
||||
SHOW_IMPORT_PAGE: 'SHOW_IMPORT_PAGE',
|
||||
SHOW_NEW_ACCOUNT_PAGE: 'SHOW_NEW_ACCOUNT_PAGE',
|
||||
SET_NEW_ACCOUNT_FORM: 'SET_NEW_ACCOUNT_FORM',
|
||||
unlockMetamask: unlockMetamask,
|
||||
unlockFailed: unlockFailed,
|
||||
showCreateVault: showCreateVault,
|
||||
showRestoreVault: showRestoreVault,
|
||||
showInitializeMenu: showInitializeMenu,
|
||||
showImportPage,
|
||||
showNewAccountPage,
|
||||
setNewAccountForm,
|
||||
createNewVaultAndKeychain: createNewVaultAndKeychain,
|
||||
createNewVaultAndRestore: createNewVaultAndRestore,
|
||||
createNewVaultInProgress: createNewVaultInProgress,
|
||||
@ -125,6 +130,7 @@ var actions = {
|
||||
sendTx: sendTx,
|
||||
signTx: signTx,
|
||||
signTokenTx: signTokenTx,
|
||||
updateTransaction,
|
||||
updateAndApproveTx,
|
||||
cancelTx: cancelTx,
|
||||
completedTx: completedTx,
|
||||
@ -244,6 +250,13 @@ var actions = {
|
||||
setFeatureFlag,
|
||||
updateFeatureFlags,
|
||||
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
|
||||
|
||||
// Network
|
||||
setNetworkEndpoints,
|
||||
updateNetworkEndpointType,
|
||||
UPDATE_NETWORK_ENDPOINT_TYPE: 'UPDATE_NETWORK_ENDPOINT_TYPE',
|
||||
|
||||
retryTransaction,
|
||||
}
|
||||
|
||||
module.exports = actions
|
||||
@ -714,6 +727,23 @@ function signTokenTx (tokenAddress, toAddress, amount, txData) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateTransaction (txData) {
|
||||
log.info('actions: updateTx: ' + JSON.stringify(txData))
|
||||
return (dispatch) => {
|
||||
log.debug(`actions calling background.updateTx`)
|
||||
background.updateTransaction(txData, (err) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
dispatch(actions.updateTransactionParams(txData.id, txData.txParams))
|
||||
if (err) {
|
||||
dispatch(actions.txError(err))
|
||||
dispatch(actions.goHome())
|
||||
return log.error(err.message)
|
||||
}
|
||||
dispatch(actions.showConfTxPage({ id: txData.id }))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function updateAndApproveTx (txData) {
|
||||
log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData))
|
||||
return (dispatch) => {
|
||||
@ -829,6 +859,7 @@ function cancelTx (txData) {
|
||||
log.debug(`background.cancelTransaction`)
|
||||
return new Promise((resolve, reject) => {
|
||||
background.cancelTransaction(txData.id, () => {
|
||||
dispatch(actions.clearSend())
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
resolve(txData)
|
||||
})
|
||||
@ -880,6 +911,20 @@ function showImportPage () {
|
||||
}
|
||||
}
|
||||
|
||||
function showNewAccountPage (formToSelect) {
|
||||
return {
|
||||
type: actions.SHOW_NEW_ACCOUNT_PAGE,
|
||||
formToSelect,
|
||||
}
|
||||
}
|
||||
|
||||
function setNewAccountForm (formToSelect) {
|
||||
return {
|
||||
type: actions.SET_NEW_ACCOUNT_FORM,
|
||||
formToSelect,
|
||||
}
|
||||
}
|
||||
|
||||
function createNewVaultInProgress () {
|
||||
return {
|
||||
type: actions.CREATE_NEW_VAULT_IN_PROGRESS,
|
||||
@ -976,9 +1021,13 @@ function lockMetamask () {
|
||||
})
|
||||
.then(newState => {
|
||||
dispatch(actions.updateMetamaskState(newState))
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
dispatch({ type: actions.LOCK_METAMASK })
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
dispatch({ type: actions.LOCK_METAMASK })
|
||||
})
|
||||
.catch(() => dispatch({ type: actions.LOCK_METAMASK }))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1123,10 +1172,12 @@ function removeToken (address) {
|
||||
function addTokens (tokens) {
|
||||
return dispatch => {
|
||||
if (Array.isArray(tokens)) {
|
||||
dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens[0])))
|
||||
return Promise.all(tokens.map(({ address, symbol, decimals }) => (
|
||||
dispatch(addToken(address, symbol, decimals))
|
||||
)))
|
||||
} else {
|
||||
dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens)))
|
||||
return Promise.all(
|
||||
Object
|
||||
.entries(tokens)
|
||||
@ -1196,6 +1247,19 @@ function markAccountsFound () {
|
||||
return callBackgroundThenUpdate(background.markAccountsFound)
|
||||
}
|
||||
|
||||
function retryTransaction (txId) {
|
||||
log.debug(`background.retryTransaction`)
|
||||
return (dispatch) => {
|
||||
background.retryTransaction(txId, (err, newState) => {
|
||||
if (err) {
|
||||
return dispatch(actions.displayWarning(err.message))
|
||||
}
|
||||
dispatch(actions.updateMetamaskState(newState))
|
||||
dispatch(actions.viewPendingTx(txId))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// config
|
||||
//
|
||||
@ -1472,7 +1536,6 @@ function pairUpdate (coin) {
|
||||
|
||||
function shapeShiftSubview (network) {
|
||||
var pair = 'btc_eth'
|
||||
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showSubLoadingIndication())
|
||||
shapeShiftRequest('marketinfo', {pair}, (mktResponse) => {
|
||||
@ -1498,7 +1561,7 @@ function coinShiftRquest (data, marketData) {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (response.error) return dispatch(actions.displayWarning(response.error))
|
||||
var message = `
|
||||
Deposit your ${response.depositType} to the address bellow:`
|
||||
Deposit your ${response.depositType} to the address below:`
|
||||
log.debug(`background.createShapeShiftTx`)
|
||||
background.createShapeShiftTx(response.deposit, response.depositType)
|
||||
dispatch(actions.showQrView(response.deposit, [message].concat(marketData)))
|
||||
@ -1534,7 +1597,7 @@ function reshowQrCode (data, coin) {
|
||||
if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error))
|
||||
|
||||
var message = [
|
||||
`Deposit your ${coin} to the address bellow:`,
|
||||
`Deposit your ${coin} to the address below:`,
|
||||
`Deposit Limit: ${mktResponse.limit}`,
|
||||
`Deposit Minimum:${mktResponse.minimum}`,
|
||||
]
|
||||
@ -1600,10 +1663,7 @@ function updateTokenExchangeRate (token = '') {
|
||||
}
|
||||
}
|
||||
|
||||
function setFeatureFlag (feature, activated) {
|
||||
const notificationType = activated
|
||||
? 'BETA_UI_NOTIFICATION_MODAL'
|
||||
: 'OLD_UI_NOTIFICATION_MODAL'
|
||||
function setFeatureFlag (feature, activated, notificationType) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -1611,10 +1671,10 @@ function setFeatureFlag (feature, activated) {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
reject(err)
|
||||
return reject(err)
|
||||
}
|
||||
dispatch(actions.updateFeatureFlags(updatedFeatureFlags))
|
||||
dispatch(actions.showModal({ name: notificationType }))
|
||||
notificationType && dispatch(actions.showModal({ name: notificationType }))
|
||||
resolve(updatedFeatureFlags)
|
||||
})
|
||||
})
|
||||
@ -1698,3 +1758,27 @@ function setUseBlockie (val) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function setNetworkEndpoints (networkEndpointType) {
|
||||
return dispatch => {
|
||||
log.debug('background.setNetworkEndpoints')
|
||||
return new Promise((resolve, reject) => {
|
||||
background.setNetworkEndpoints(networkEndpointType, err => {
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
dispatch(actions.updateNetworkEndpointType(networkEndpointType))
|
||||
resolve(networkEndpointType)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function updateNetworkEndpointType (networkEndpointType) {
|
||||
return {
|
||||
type: actions.UPDATE_NETWORK_ENDPOINT_TYPE,
|
||||
value: networkEndpointType,
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ const { Switch, Redirect, withRouter } = require('react-router-dom')
|
||||
const { compose } = require('recompose')
|
||||
const h = require('react-hyperscript')
|
||||
const actions = require('./actions')
|
||||
const classnames = require('classnames')
|
||||
|
||||
// mascara
|
||||
const MascaraCreatePassword = require('../../mascara/src/app/first-time/create-password-screen').default
|
||||
const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ether-screen').default
|
||||
@ -234,22 +236,22 @@ class App extends Component {
|
||||
showNetworkDropdown,
|
||||
hideNetworkDropdown,
|
||||
currentView,
|
||||
isMascara,
|
||||
isOnboarding,
|
||||
history,
|
||||
} = this.props
|
||||
|
||||
if (window.METAMASK_UI_TYPE === 'notification') {
|
||||
return null
|
||||
}
|
||||
|
||||
const props = this.props
|
||||
const {isMascara, isOnboarding} = props
|
||||
|
||||
// Do not render header if user is in mascara onboarding
|
||||
if (isMascara && isOnboarding) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Do not render header if user is in mascara buy ether
|
||||
if (isMascara && currentView.name === 'buyEth') {
|
||||
if (isMascara && props.currentView.name === 'buyEth') {
|
||||
return null
|
||||
}
|
||||
|
||||
@ -260,7 +262,9 @@ class App extends Component {
|
||||
}, [
|
||||
|
||||
h('.app-header.flex-row.flex-space-between', {
|
||||
style: {},
|
||||
className: classnames({
|
||||
'app-header--initialized': !isOnboarding,
|
||||
}),
|
||||
}, [
|
||||
h('div.app-header-contents', {}, [
|
||||
h('div.left-menu-wrapper', {
|
||||
@ -268,19 +272,13 @@ class App extends Component {
|
||||
}, [
|
||||
// mini logo
|
||||
h('img.metafox-icon', {
|
||||
height: 29,
|
||||
width: 29,
|
||||
src: '/images/icon-128.png',
|
||||
height: 42,
|
||||
width: 42,
|
||||
src: '/images/metamask-fox.svg',
|
||||
}),
|
||||
|
||||
// metamask name
|
||||
h('h1', {
|
||||
style: {
|
||||
position: 'relative',
|
||||
paddingLeft: '9px',
|
||||
color: '#5B5D67',
|
||||
},
|
||||
}, 'MetaMask'),
|
||||
h('h1', 'MetaMask'),
|
||||
|
||||
]),
|
||||
|
||||
@ -313,6 +311,7 @@ class App extends Component {
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
])
|
||||
)
|
||||
}
|
||||
|
@ -33,15 +33,28 @@ function mapDispatchToProps (dispatch) {
|
||||
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
|
||||
showAccountDetail: address => {
|
||||
dispatch(actions.showAccountDetail(address))
|
||||
dispatch(actions.hideSidebar())
|
||||
dispatch(actions.toggleAccountMenu())
|
||||
},
|
||||
lockMetamask: () => {
|
||||
dispatch(actions.lockMetamask())
|
||||
dispatch(actions.displayWarning(null))
|
||||
dispatch(actions.hideWarning())
|
||||
dispatch(actions.hideSidebar())
|
||||
dispatch(actions.toggleAccountMenu())
|
||||
},
|
||||
showNewAccountModal: () => {
|
||||
dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
|
||||
showConfigPage: () => {
|
||||
dispatch(actions.showConfigPage())
|
||||
dispatch(actions.hideSidebar())
|
||||
dispatch(actions.toggleAccountMenu())
|
||||
},
|
||||
showNewAccountPage: (formToSelect) => {
|
||||
dispatch(actions.showNewAccountPage(formToSelect))
|
||||
dispatch(actions.hideSidebar())
|
||||
dispatch(actions.toggleAccountMenu())
|
||||
},
|
||||
showInfoPage: () => {
|
||||
dispatch(actions.showInfoPage())
|
||||
dispatch(actions.hideSidebar())
|
||||
dispatch(actions.toggleAccountMenu())
|
||||
},
|
||||
}
|
||||
@ -51,7 +64,7 @@ AccountMenu.prototype.render = function () {
|
||||
const {
|
||||
isAccountMenuOpen,
|
||||
toggleAccountMenu,
|
||||
showNewAccountModal,
|
||||
showNewAccountPage,
|
||||
lockMetamask,
|
||||
history,
|
||||
} = this.props
|
||||
@ -73,15 +86,12 @@ AccountMenu.prototype.render = function () {
|
||||
h('div.account-menu__accounts', this.renderAccounts()),
|
||||
h(Divider),
|
||||
h(Item, {
|
||||
onClick: showNewAccountModal,
|
||||
onClick: () => showNewAccountPage('CREATE'),
|
||||
icon: h('img', { src: 'images/plus-btn-white.svg' }),
|
||||
text: 'Create Account',
|
||||
}),
|
||||
h(Item, {
|
||||
onClick: () => {
|
||||
toggleAccountMenu()
|
||||
history.push(IMPORT_ACCOUNT_ROUTE)
|
||||
},
|
||||
onClick: () => showNewAccountPage('IMPORT'),
|
||||
icon: h('img', { src: 'images/import-account.svg' }),
|
||||
text: 'Import Account',
|
||||
}),
|
||||
|
@ -40,7 +40,7 @@ BalanceComponent.prototype.render = function () {
|
||||
// style: {},
|
||||
// }),
|
||||
h(Identicon, {
|
||||
diameter: 45,
|
||||
diameter: 50,
|
||||
address: token && token.address,
|
||||
network,
|
||||
}),
|
||||
@ -94,7 +94,8 @@ BalanceComponent.prototype.renderFiatValue = function (formattedBalance) {
|
||||
}
|
||||
|
||||
BalanceComponent.prototype.renderFiatAmount = function (fiatDisplayNumber, fiatSuffix, fiatPrefix) {
|
||||
if (fiatDisplayNumber === 'N/A') return null
|
||||
const shouldNotRenderFiat = fiatDisplayNumber === 'N/A' || Number(fiatDisplayNumber) === 0
|
||||
if (shouldNotRenderFiat) return null
|
||||
|
||||
return h('div.fiat-amount', {
|
||||
style: {},
|
||||
|
@ -40,7 +40,7 @@ CoinbaseForm.prototype.render = function () {
|
||||
}, 'Continue to Coinbase'),
|
||||
|
||||
h('button.btn-red', {
|
||||
onClick: () => props.dispatch(actions.backTobuyView(props.accounts.address)),
|
||||
onClick: () => props.dispatch(actions.goHome()),
|
||||
}, 'Cancel'),
|
||||
]),
|
||||
])
|
||||
|
@ -50,10 +50,18 @@ function sanitizeValue (value) {
|
||||
|
||||
CurrencyInput.prototype.handleChange = function (newValue) {
|
||||
const { onInputChange } = this.props
|
||||
const { value } = this.state
|
||||
|
||||
this.setState({ value: sanitizeValue(newValue) })
|
||||
let parsedValue = newValue
|
||||
const newValueLastIndex = newValue.length - 1
|
||||
|
||||
onInputChange(sanitizeValue(newValue))
|
||||
if (value === '0' && newValue[newValueLastIndex] === '0') {
|
||||
parsedValue = parsedValue.slice(0, newValueLastIndex)
|
||||
}
|
||||
|
||||
const sanitizedValue = sanitizeValue(parsedValue)
|
||||
this.setState({ value: sanitizedValue })
|
||||
onInputChange(sanitizedValue)
|
||||
}
|
||||
|
||||
// If state.value === props.value plus a decimal point, or at least one
|
||||
|
@ -199,7 +199,7 @@ class AccountDropdowns extends Component {
|
||||
{},
|
||||
menuItemStyles,
|
||||
),
|
||||
onClick: () => actions.showNewAccountModal(),
|
||||
onClick: () => actions.showNewAccountPageCreateForm(),
|
||||
},
|
||||
[
|
||||
h(
|
||||
@ -228,7 +228,7 @@ class AccountDropdowns extends Component {
|
||||
actions.hideSidebar()
|
||||
}
|
||||
},
|
||||
onClick: () => actions.showImportPage(),
|
||||
onClick: () => actions.showNewAccountPageImportForm(),
|
||||
style: Object.assign(
|
||||
{},
|
||||
menuItemStyles,
|
||||
@ -457,9 +457,7 @@ const mapDispatchToProps = (dispatch) => {
|
||||
identity,
|
||||
}))
|
||||
},
|
||||
showNewAccountModal: () => {
|
||||
dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
|
||||
},
|
||||
showNewAccountPageCreateForm: () => dispatch(actions.showNewAccountPage({ form: 'CREATE' })),
|
||||
showExportPrivateKeyModal: () => {
|
||||
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
|
||||
},
|
||||
@ -467,7 +465,7 @@ const mapDispatchToProps = (dispatch) => {
|
||||
dispatch(actions.showAddTokenPage())
|
||||
},
|
||||
addNewAccount: () => dispatch(actions.addNewAccount()),
|
||||
showImportPage: () => dispatch(actions.showImportPage()),
|
||||
showNewAccountPageImportForm: () => dispatch(actions.showNewAccountPage({ form: 'IMPORT' })),
|
||||
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
|
||||
},
|
||||
}
|
||||
|
@ -7,13 +7,13 @@ const debounce = require('debounce')
|
||||
module.exports = Mascot
|
||||
|
||||
inherits(Mascot, Component)
|
||||
function Mascot () {
|
||||
function Mascot ({width = '200', height = '200'}) {
|
||||
Component.call(this)
|
||||
this.logo = metamaskLogo({
|
||||
followMouse: true,
|
||||
pxNotRatio: true,
|
||||
width: 200,
|
||||
height: 200,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
|
||||
this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000)
|
||||
|
@ -62,12 +62,12 @@ AccountDetailsModal.prototype.render = function () {
|
||||
|
||||
h('div.account-modal-divider'),
|
||||
|
||||
h('button.btn-clear', {
|
||||
h('button.btn-clear.account-modal__button', {
|
||||
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
|
||||
}, 'View account on Etherscan'),
|
||||
|
||||
// Holding on redesign for Export Private Key functionality
|
||||
h('button.btn-clear', {
|
||||
h('button.btn-clear.account-modal__button', {
|
||||
onClick: () => showExportPrivateKeyModal(),
|
||||
}, 'Export private key'),
|
||||
|
||||
|
@ -69,7 +69,7 @@ BuyOptions.prototype.render = function () {
|
||||
// h('div.buy-modal-content-option', {}, [
|
||||
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
|
||||
// h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'),
|
||||
// ]),
|
||||
// ]),,
|
||||
|
||||
this.renderModalContentOption(
|
||||
'Direct Deposit',
|
||||
|
184
ui/app/components/modals/deposit-ether-modal.js
Normal file
184
ui/app/components/modals/deposit-ether-modal.js
Normal file
@ -0,0 +1,184 @@
|
||||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../actions')
|
||||
const networkNames = require('../../../../app/scripts/config.js').networkNames
|
||||
const ShapeshiftForm = require('../shapeshift-form')
|
||||
|
||||
const DIRECT_DEPOSIT_ROW_TITLE = 'Directly Deposit Ether'
|
||||
const DIRECT_DEPOSIT_ROW_TEXT = `If you already have some Ether, the quickest way to get Ether in
|
||||
your new wallet by direct deposit.`
|
||||
const COINBASE_ROW_TITLE = 'Buy on Coinbase'
|
||||
const COINBASE_ROW_TEXT = `Coinbase is the world’s most popular way to buy and sell bitcoin,
|
||||
ethereum, and litecoin.`
|
||||
const SHAPESHIFT_ROW_TITLE = 'Deposit with ShapeShift'
|
||||
const SHAPESHIFT_ROW_TEXT = `If you own other cryptocurrencies, you can trade and deposit Ether
|
||||
directly into your MetaMask wallet. No Account Needed.`
|
||||
const FAUCET_ROW_TITLE = 'Test Faucet'
|
||||
const facuetRowText = networkName => `Get Ether from a faucet for the ${networkName}`
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
network: state.metamask.network,
|
||||
address: state.metamask.selectedAddress,
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
toCoinbase: (address) => {
|
||||
dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
|
||||
},
|
||||
hideModal: () => {
|
||||
dispatch(actions.hideModal())
|
||||
},
|
||||
showAccountDetailModal: () => {
|
||||
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
|
||||
},
|
||||
toFaucet: network => dispatch(actions.buyEth({ network })),
|
||||
}
|
||||
}
|
||||
|
||||
inherits(DepositEtherModal, Component)
|
||||
function DepositEtherModal () {
|
||||
Component.call(this)
|
||||
|
||||
this.state = {
|
||||
buyingWithShapeshift: false,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(DepositEtherModal)
|
||||
|
||||
DepositEtherModal.prototype.renderRow = function ({
|
||||
logo,
|
||||
title,
|
||||
text,
|
||||
buttonLabel,
|
||||
onButtonClick,
|
||||
hide,
|
||||
className,
|
||||
hideButton,
|
||||
hideTitle,
|
||||
onBackClick,
|
||||
showBackButton,
|
||||
}) {
|
||||
if (hide) {
|
||||
return null
|
||||
}
|
||||
|
||||
return h('div', {
|
||||
className: className || 'deposit-ether-modal__buy-row',
|
||||
}, [
|
||||
|
||||
onBackClick && showBackButton && h('div.deposit-ether-modal__buy-row__back', {
|
||||
onClick: onBackClick,
|
||||
}, [
|
||||
|
||||
h('i.fa.fa-arrow-left.cursor-pointer'),
|
||||
|
||||
]),
|
||||
|
||||
h('div.deposit-ether-modal__buy-row__logo', [logo]),
|
||||
|
||||
h('div.deposit-ether-modal__buy-row__description', [
|
||||
|
||||
!hideTitle && h('div.deposit-ether-modal__buy-row__description__title', [title]),
|
||||
|
||||
h('div.deposit-ether-modal__buy-row__description__text', [text]),
|
||||
|
||||
]),
|
||||
|
||||
!hideButton && h('div.deposit-ether-modal__buy-row__button', [
|
||||
h('button.deposit-ether-modal__deposit-button', {
|
||||
onClick: onButtonClick,
|
||||
}, [buttonLabel]),
|
||||
]),
|
||||
|
||||
])
|
||||
}
|
||||
|
||||
DepositEtherModal.prototype.render = function () {
|
||||
const { network, toCoinbase, address, toFaucet } = this.props
|
||||
const { buyingWithShapeshift } = this.state
|
||||
|
||||
const isTestNetwork = ['3', '4', '42'].find(n => n === network)
|
||||
const networkName = networkNames[network]
|
||||
|
||||
return h('div.deposit-ether-modal', {}, [
|
||||
|
||||
h('div.deposit-ether-modal__header', [
|
||||
|
||||
h('div.deposit-ether-modal__header__title', ['Deposit Ether']),
|
||||
|
||||
h('div.deposit-ether-modal__header__description', [
|
||||
'To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet.',
|
||||
]),
|
||||
|
||||
h('div.deposit-ether-modal__header__close', {
|
||||
onClick: () => {
|
||||
this.setState({ buyingWithShapeshift: false })
|
||||
this.props.hideModal()
|
||||
},
|
||||
}),
|
||||
|
||||
]),
|
||||
|
||||
h('div.deposit-ether-modal__buy-rows', [
|
||||
|
||||
this.renderRow({
|
||||
logo: h('img.deposit-ether-modal__buy-row__eth-logo', { src: '../../../images/eth_logo.svg' }),
|
||||
title: DIRECT_DEPOSIT_ROW_TITLE,
|
||||
text: DIRECT_DEPOSIT_ROW_TEXT,
|
||||
buttonLabel: 'View Account',
|
||||
onButtonClick: () => this.goToAccountDetailsModal(),
|
||||
hide: buyingWithShapeshift,
|
||||
}),
|
||||
|
||||
this.renderRow({
|
||||
logo: h('i.fa.fa-tint.fa-2x'),
|
||||
title: FAUCET_ROW_TITLE,
|
||||
text: facuetRowText(networkName),
|
||||
buttonLabel: 'Get Ether',
|
||||
onButtonClick: () => toFaucet(network),
|
||||
hide: !isTestNetwork || buyingWithShapeshift,
|
||||
}),
|
||||
|
||||
this.renderRow({
|
||||
logo: h('img.deposit-ether-modal__buy-row__coinbase-logo', {
|
||||
src: '../../../images/coinbase logo.png',
|
||||
}),
|
||||
title: COINBASE_ROW_TITLE,
|
||||
text: COINBASE_ROW_TEXT,
|
||||
buttonLabel: 'Continue to Coinbase',
|
||||
onButtonClick: () => toCoinbase(address),
|
||||
hide: isTestNetwork || buyingWithShapeshift,
|
||||
}),
|
||||
|
||||
this.renderRow({
|
||||
logo: h('img.deposit-ether-modal__buy-row__shapeshift-logo', {
|
||||
src: '../../../images/shapeshift logo.png',
|
||||
}),
|
||||
title: SHAPESHIFT_ROW_TITLE,
|
||||
text: SHAPESHIFT_ROW_TEXT,
|
||||
buttonLabel: 'Buy with Shapeshift',
|
||||
onButtonClick: () => this.setState({ buyingWithShapeshift: true }),
|
||||
hide: isTestNetwork,
|
||||
hideButton: buyingWithShapeshift,
|
||||
hideTitle: buyingWithShapeshift,
|
||||
onBackClick: () => this.setState({ buyingWithShapeshift: false }),
|
||||
showBackButton: this.state.buyingWithShapeshift,
|
||||
className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy',
|
||||
}),
|
||||
|
||||
buyingWithShapeshift && h(ShapeshiftForm),
|
||||
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
DepositEtherModal.prototype.goToAccountDetailsModal = function () {
|
||||
this.props.hideModal()
|
||||
this.props.showAccountDetailModal()
|
||||
}
|
@ -79,11 +79,15 @@ ExportPrivateKeyModal.prototype.renderButton = function (className, onClick, lab
|
||||
|
||||
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) {
|
||||
return h('div.export-private-key-buttons', {}, [
|
||||
!privateKey && this.renderButton('btn-clear btn-cancel', () => hideModal(), 'Cancel'),
|
||||
!privateKey && this.renderButton(
|
||||
'btn-cancel export-private-key__button export-private-key__button--cancel',
|
||||
() => hideModal(),
|
||||
'Cancel'
|
||||
),
|
||||
|
||||
(privateKey
|
||||
? this.renderButton('btn-clear', () => hideModal(), 'Done')
|
||||
: this.renderButton('btn-clear', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Show')
|
||||
? this.renderButton('btn-clear export-private-key__button', () => hideModal(), 'Done')
|
||||
: this.renderButton('btn-clear export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Confirm')
|
||||
),
|
||||
|
||||
])
|
||||
|
@ -58,12 +58,12 @@ HideTokenConfirmationModal.prototype.render = function () {
|
||||
]),
|
||||
|
||||
h('div.hide-token-confirmation__buttons', {}, [
|
||||
h('button.btn-clear', {
|
||||
h('button.btn-cancel.hide-token-confirmation__button', {
|
||||
onClick: () => hideModal(),
|
||||
}, [
|
||||
'CANCEL',
|
||||
]),
|
||||
h('button.btn-clear', {
|
||||
h('button.btn-clear.hide-token-confirmation__button', {
|
||||
onClick: () => hideToken(address),
|
||||
}, [
|
||||
'HIDE',
|
||||
|
@ -9,6 +9,7 @@ const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-n
|
||||
|
||||
// Modal Components
|
||||
const BuyOptions = require('./buy-options-modal')
|
||||
const DepositEtherModal = require('./deposit-ether-modal')
|
||||
const AccountDetailsModal = require('./account-details-modal')
|
||||
const EditAccountNameModal = require('./edit-account-name-modal')
|
||||
const ExportPrivateKeyModal = require('./export-private-key-modal')
|
||||
@ -73,6 +74,37 @@ const MODALS = {
|
||||
},
|
||||
},
|
||||
|
||||
DEPOSIT_ETHER: {
|
||||
contents: [
|
||||
h(DepositEtherModal, {}, []),
|
||||
],
|
||||
mobileModalStyle: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
transform: 'none',
|
||||
left: '0',
|
||||
right: '0',
|
||||
margin: '0 auto',
|
||||
boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)',
|
||||
top: '0',
|
||||
display: 'flex',
|
||||
},
|
||||
laptopModalStyle: {
|
||||
width: '900px',
|
||||
maxWidth: '900px',
|
||||
top: 'calc(10% + 10px)',
|
||||
left: '0',
|
||||
right: '0',
|
||||
margin: '0 auto',
|
||||
boxShadow: '0 0 6px 0 rgba(0,0,0,0.3)',
|
||||
borderRadius: '8px',
|
||||
transform: 'none',
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
EDIT_ACCOUNT_NAME: {
|
||||
contents: [
|
||||
h(EditAccountNameModal, {}, []),
|
||||
|
@ -39,7 +39,6 @@ Network.prototype.render = function () {
|
||||
},
|
||||
src: 'images/loading.svg',
|
||||
}),
|
||||
h('i.fa.fa-caret-down.network-caret'),
|
||||
])
|
||||
} else if (providerName === 'mainnet') {
|
||||
hoverText = 'Main Ethereum Network'
|
||||
@ -85,12 +84,8 @@ Network.prototype.render = function () {
|
||||
backgroundColor: '#038789', // $blue-lagoon
|
||||
nonSelectBackgroundColor: '#15afb2',
|
||||
}),
|
||||
h('.network-name', {
|
||||
style: {
|
||||
color: '#039396',
|
||||
}},
|
||||
'Main Network'),
|
||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
||||
h('.network-name', 'Main Network'),
|
||||
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||
])
|
||||
case 'ropsten-test-network':
|
||||
return h('.network-indicator', [
|
||||
@ -98,12 +93,8 @@ Network.prototype.render = function () {
|
||||
backgroundColor: '#e91550', // $crimson
|
||||
nonSelectBackgroundColor: '#ec2c50',
|
||||
}),
|
||||
h('.network-name', {
|
||||
style: {
|
||||
color: '#ff6666',
|
||||
}},
|
||||
'Ropsten Test Net'),
|
||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
||||
h('.network-name', 'Ropsten Test Net'),
|
||||
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||
])
|
||||
case 'kovan-test-network':
|
||||
return h('.network-indicator', [
|
||||
@ -111,12 +102,8 @@ Network.prototype.render = function () {
|
||||
backgroundColor: '#690496', // $purple
|
||||
nonSelectBackgroundColor: '#b039f3',
|
||||
}),
|
||||
h('.network-name', {
|
||||
style: {
|
||||
color: '#690496',
|
||||
}},
|
||||
'Kovan Test Net'),
|
||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
||||
h('.network-name', 'Kovan Test Net'),
|
||||
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||
])
|
||||
case 'rinkeby-test-network':
|
||||
return h('.network-indicator', [
|
||||
@ -124,12 +111,8 @@ Network.prototype.render = function () {
|
||||
backgroundColor: '#ebb33f', // $tulip-tree
|
||||
nonSelectBackgroundColor: '#ecb23e',
|
||||
}),
|
||||
h('.network-name', {
|
||||
style: {
|
||||
color: '#e7a218',
|
||||
}},
|
||||
'Rinkeby Test Net'),
|
||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
||||
h('.network-name', 'Rinkeby Test Net'),
|
||||
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||
])
|
||||
default:
|
||||
return h('.network-indicator', [
|
||||
@ -140,12 +123,8 @@ Network.prototype.render = function () {
|
||||
},
|
||||
}),
|
||||
|
||||
h('.network-name', {
|
||||
style: {
|
||||
color: '#AEAEAE',
|
||||
}},
|
||||
'Private Network'),
|
||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
||||
h('.network-name', 'Private Network'),
|
||||
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||
])
|
||||
}
|
||||
})(),
|
||||
|
@ -3,6 +3,7 @@ const Component = require('react').Component
|
||||
const classnames = require('classnames')
|
||||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const R = require('ramda')
|
||||
const Fuse = require('fuse.js')
|
||||
const contractMap = require('eth-contract-metadata')
|
||||
const TokenBalance = require('../../components/token-balance')
|
||||
@ -17,7 +18,10 @@ const fuse = new Fuse(contractList, {
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: ['address', 'name', 'symbol'],
|
||||
keys: [
|
||||
{ name: 'name', weight: 0.5 },
|
||||
{ name: 'symbol', weight: 0.5 },
|
||||
],
|
||||
})
|
||||
// const actions = require('./actions')
|
||||
const actions = require('../../actions')
|
||||
@ -219,9 +223,11 @@ AddTokenScreen.prototype.renderCustomForm = function () {
|
||||
|
||||
AddTokenScreen.prototype.renderTokenList = function () {
|
||||
const { searchQuery = '', selectedTokens } = this.state
|
||||
const results = searchQuery
|
||||
? fuse.search(searchQuery) || []
|
||||
: contractList
|
||||
const fuseSearchResult = fuse.search(searchQuery)
|
||||
const addressSearchResult = contractList.filter(token => {
|
||||
return token.address.toLowerCase() === searchQuery.toLowerCase()
|
||||
})
|
||||
const results = [...addressSearchResult, ...fuseSearchResult]
|
||||
|
||||
return Array(6).fill(undefined)
|
||||
.map((_, i) => {
|
||||
@ -297,12 +303,12 @@ AddTokenScreen.prototype.renderConfirmation = function () {
|
||||
]),
|
||||
]),
|
||||
h('div.add-token__buttons', [
|
||||
h('button.btn-secondary', {
|
||||
onClick: () => addTokens(tokens).then(() => history.push(DEFAULT_ROUTE)),
|
||||
}, 'Add Tokens'),
|
||||
h('button.btn-tertiary', {
|
||||
h('button.btn-cancel.add-token__button', {
|
||||
onClick: () => this.setState({ isShowingConfirmation: false }),
|
||||
}, 'Back'),
|
||||
h('button.btn-clear.add-token__button', {
|
||||
onClick: () => addTokens(tokens).then(() => history.push(DEFAULT_ROUTE)),
|
||||
}, 'Add Tokens'),
|
||||
]),
|
||||
])
|
||||
)
|
||||
@ -347,12 +353,12 @@ AddTokenScreen.prototype.render = function () {
|
||||
]),
|
||||
]),
|
||||
h('div.add-token__buttons', [
|
||||
h('button.btn-secondary', {
|
||||
onClick: this.onNext,
|
||||
}, 'Next'),
|
||||
h('button.btn-tertiary', {
|
||||
h('button.btn-cancel.add-token__button', {
|
||||
onClick: () => history.goBack(),
|
||||
}, 'Cancel'),
|
||||
h('button.btn-clear.add-token__button', {
|
||||
onClick: this.onNext,
|
||||
}, 'Next'),
|
||||
]),
|
||||
])
|
||||
)
|
||||
|
@ -24,14 +24,7 @@ JsonImportSubview.prototype.render = function () {
|
||||
const { error } = this.props
|
||||
|
||||
return (
|
||||
h('div', {
|
||||
style: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '5px 15px 0px 15px',
|
||||
},
|
||||
}, [
|
||||
h('div.new-account-import-form__json', [
|
||||
|
||||
h('p', 'Used by a variety of different clients'),
|
||||
h('a.warning', { href: HELP_LINK, target: '_blank' }, 'File import not working? Click here!'),
|
||||
@ -40,28 +33,35 @@ JsonImportSubview.prototype.render = function () {
|
||||
readAs: 'text',
|
||||
onLoad: this.onLoad.bind(this),
|
||||
style: {
|
||||
margin: '20px 0px 12px 20px',
|
||||
margin: '20px 0px 12px 34%',
|
||||
fontSize: '15px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}),
|
||||
|
||||
h('input.large-input.letter-spacey', {
|
||||
h('input.new-account-import-form__input-password', {
|
||||
type: 'password',
|
||||
placeholder: 'Enter password',
|
||||
id: 'json-password-box',
|
||||
onKeyPress: this.createKeyringOnEnter.bind(this),
|
||||
style: {
|
||||
width: 260,
|
||||
marginTop: 12,
|
||||
},
|
||||
}),
|
||||
|
||||
h('button.primary', {
|
||||
onClick: this.createNewKeychain.bind(this),
|
||||
style: {
|
||||
margin: 12,
|
||||
},
|
||||
}, 'Import'),
|
||||
h('div.new-account-create-form__buttons', {}, [
|
||||
|
||||
h('button.new-account-create-form__button-cancel', {
|
||||
onClick: () => this.props.goHome(),
|
||||
}, [
|
||||
'CANCEL',
|
||||
]),
|
||||
|
||||
h('button.new-account-create-form__button-create', {
|
||||
onClick: () => this.createNewKeychain.bind(this),
|
||||
}, [
|
||||
'IMPORT',
|
||||
]),
|
||||
|
||||
]),
|
||||
|
||||
error ? h('span.error', error) : null,
|
||||
])
|
||||
|
@ -4,7 +4,7 @@ const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../../actions')
|
||||
|
||||
module.exports = connect(mapStateToProps)(PrivateKeyImportView)
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(PrivateKeyImportView)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
@ -12,45 +12,49 @@ function mapStateToProps (state) {
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
goHome: () => dispatch(actions.goHome()),
|
||||
importNewAccount: (strategy, [ privateKey ]) => {
|
||||
dispatch(actions.importNewAccount(strategy, [ privateKey ]))
|
||||
},
|
||||
displayWarning: () => dispatch(actions.displayWarning(null)),
|
||||
}
|
||||
}
|
||||
|
||||
inherits(PrivateKeyImportView, Component)
|
||||
function PrivateKeyImportView () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
PrivateKeyImportView.prototype.componentWillUnmount = function () {
|
||||
this.props.dispatch(actions.displayWarning(null))
|
||||
}
|
||||
|
||||
PrivateKeyImportView.prototype.render = function () {
|
||||
const { error } = this.props
|
||||
const { error, goHome } = this.props
|
||||
|
||||
return (
|
||||
h('div', {
|
||||
style: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '5px 15px 0px 15px',
|
||||
},
|
||||
}, [
|
||||
h('span', 'Paste your private key string here'),
|
||||
h('div.new-account-import-form__private-key', [
|
||||
h('span.new-account-create-form__instruction', 'Paste your private key string here:'),
|
||||
|
||||
h('input.large-input.letter-spacey', {
|
||||
h('input.new-account-import-form__input-password', {
|
||||
type: 'password',
|
||||
id: 'private-key-box',
|
||||
onKeyPress: this.createKeyringOnEnter.bind(this),
|
||||
style: {
|
||||
width: 260,
|
||||
marginTop: 12,
|
||||
},
|
||||
onKeyPress: () => this.createKeyringOnEnter(),
|
||||
}),
|
||||
|
||||
h('button.primary', {
|
||||
onClick: this.createNewKeychain.bind(this),
|
||||
style: {
|
||||
margin: 12,
|
||||
},
|
||||
}, 'Import'),
|
||||
h('div.new-account-create-form__buttons', {}, [
|
||||
|
||||
h('button.new-account-create-form__button-cancel', {
|
||||
onClick: () => goHome(),
|
||||
}, [
|
||||
'CANCEL',
|
||||
]),
|
||||
|
||||
h('button.new-account-create-form__button-create', {
|
||||
onClick: () => this.createNewKeychain(),
|
||||
}, [
|
||||
'IMPORT',
|
||||
]),
|
||||
|
||||
]),
|
||||
|
||||
error ? h('span.error', error) : null,
|
||||
])
|
||||
@ -67,5 +71,6 @@ PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
|
||||
PrivateKeyImportView.prototype.createNewKeychain = function () {
|
||||
const input = document.getElementById('private-key-box')
|
||||
const privateKey = input.value
|
||||
this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
|
||||
|
||||
this.props.importNewAccount('Private Key', [ privateKey ])
|
||||
}
|
||||
|
@ -52,7 +52,10 @@ class RestoreVaultPage extends PersistentForm {
|
||||
// submit
|
||||
this.props.createNewVaultAndRestore(password, seed)
|
||||
.then(() => history.push(DEFAULT_ROUTE))
|
||||
.catch(({ message }) => this.setState({ error: message }))
|
||||
.catch(({ message }) => {
|
||||
this.setState({ error: message })
|
||||
log.error(message)
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -11,6 +11,7 @@ const { exportAsFile } = require('../../../util')
|
||||
const SimpleDropdown = require('../../dropdowns/simple-dropdown')
|
||||
const ToggleButton = require('react-toggle-button')
|
||||
const { REVEAL_SEED_ROUTE } = require('../../../routes')
|
||||
const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
|
||||
|
||||
const getInfuraCurrencyOptions = () => {
|
||||
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
|
||||
@ -230,18 +231,18 @@ class Settings extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { warning } = this.props
|
||||
const { warning, isMascara } = this.props
|
||||
|
||||
return (
|
||||
h('div.settings__content', [
|
||||
warning && h('div.settings__error', warning),
|
||||
this.renderBlockieOptIn(),
|
||||
this.renderCurrentConversion(),
|
||||
// this.renderCurrentProvider(),
|
||||
this.renderNewRpcUrl(),
|
||||
this.renderStateLogs(),
|
||||
this.renderSeedWords(),
|
||||
this.renderOldUI(),
|
||||
!isMascara && this.renderOldUI(),
|
||||
this.renderBlockieOptIn(),
|
||||
])
|
||||
)
|
||||
}
|
||||
@ -257,12 +258,14 @@ Settings.propTypes = {
|
||||
setFeatureFlagToBeta: PropTypes.func,
|
||||
warning: PropTypes.string,
|
||||
history: PropTypes.object,
|
||||
isMascara: PropTypes.bool,
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
metamask: state.metamask,
|
||||
warning: state.appState.warning,
|
||||
isMascara: state.metamask.isMascara,
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +276,10 @@ const mapDispatchToProps = dispatch => {
|
||||
displayWarning: warning => dispatch(actions.displayWarning(warning)),
|
||||
revealSeedConfirmation: () => dispatch(actions.revealSeedConfirmation()),
|
||||
setUseBlockie: value => dispatch(actions.setUseBlockie(value)),
|
||||
setFeatureFlagToBeta: () => dispatch(actions.setFeatureFlag('betaUI', false)),
|
||||
setFeatureFlagToBeta: () => {
|
||||
return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
|
||||
.then(() => dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,8 +231,8 @@ ConfirmSendEther.prototype.render = function () {
|
||||
// Main Send token Card
|
||||
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
||||
h('h3.flex-center.confirm-screen-header', [
|
||||
h('button.confirm-screen-back-button', {
|
||||
onClick: () => this.editTransaction(txMeta),
|
||||
h('button.btn-clear.confirm-screen-back-button', {
|
||||
onClick: () => editTransaction(txMeta),
|
||||
}, 'EDIT'),
|
||||
h('div.confirm-screen-title', 'Confirm Transaction'),
|
||||
h('div.confirm-screen-header-tip'),
|
||||
@ -433,7 +433,9 @@ ConfirmSendEther.prototype.onSubmit = function (event) {
|
||||
|
||||
ConfirmSendEther.prototype.cancel = function (event, txMeta) {
|
||||
event.preventDefault()
|
||||
this.props.cancelTransaction(txMeta)
|
||||
const { cancelTransaction } = this.props
|
||||
|
||||
cancelTransaction(txMeta)
|
||||
.then(() => this.props.history.push(DEFAULT_ROUTE))
|
||||
}
|
||||
|
||||
@ -458,26 +460,6 @@ ConfirmSendEther.prototype.gatherTxMeta = function () {
|
||||
const state = this.state
|
||||
const txData = clone(state.txData) || clone(props.txData)
|
||||
|
||||
if (props.send.editingTransactionId) {
|
||||
const {
|
||||
send: {
|
||||
memo,
|
||||
amount: value,
|
||||
gasLimit: gas,
|
||||
gasPrice,
|
||||
},
|
||||
} = props
|
||||
const { txParams: { from, to } } = txData
|
||||
txData.txParams = {
|
||||
from: ethUtil.addHexPrefix(from),
|
||||
to: ethUtil.addHexPrefix(to),
|
||||
memo: memo && ethUtil.addHexPrefix(memo),
|
||||
value: ethUtil.addHexPrefix(value),
|
||||
gas: ethUtil.addHexPrefix(gas),
|
||||
gasPrice: ethUtil.addHexPrefix(gasPrice),
|
||||
}
|
||||
}
|
||||
|
||||
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
||||
return txData
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ const { withRouter } = require('react-router-dom')
|
||||
const { compose } = require('recompose')
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const ethAbi = require('ethereumjs-abi')
|
||||
const tokenAbi = require('human-standard-token-abi')
|
||||
const abiDecoder = require('abi-decoder')
|
||||
abiDecoder.addABI(tokenAbi)
|
||||
@ -305,6 +304,7 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
|
||||
}
|
||||
|
||||
ConfirmSendToken.prototype.render = function () {
|
||||
const { editTransaction } = this.props
|
||||
const txMeta = this.gatherTxMeta()
|
||||
const {
|
||||
from: {
|
||||
@ -326,8 +326,8 @@ ConfirmSendToken.prototype.render = function () {
|
||||
// Main Send token Card
|
||||
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
||||
h('h3.flex-center.confirm-screen-header', [
|
||||
h('button.confirm-screen-back-button', {
|
||||
onClick: () => this.editTransaction(txMeta),
|
||||
h('button.btn-clear.confirm-screen-back-button', {
|
||||
onClick: () => editTransaction(txMeta),
|
||||
}, 'EDIT'),
|
||||
h('div.confirm-screen-title', 'Confirm Transaction'),
|
||||
h('div.confirm-screen-header-tip'),
|
||||
@ -426,7 +426,9 @@ ConfirmSendToken.prototype.onSubmit = function (event) {
|
||||
|
||||
ConfirmSendToken.prototype.cancel = function (event, txMeta) {
|
||||
event.preventDefault()
|
||||
this.props.cancelTransaction(txMeta)
|
||||
const { cancelTransaction } = this.props
|
||||
|
||||
cancelTransaction(txMeta)
|
||||
.then(() => this.props.history.push(DEFAULT_ROUTE))
|
||||
}
|
||||
|
||||
@ -451,39 +453,6 @@ ConfirmSendToken.prototype.gatherTxMeta = function () {
|
||||
const state = this.state
|
||||
const txData = clone(state.txData) || clone(props.txData)
|
||||
|
||||
if (props.send.editingTransactionId) {
|
||||
const {
|
||||
send: {
|
||||
memo,
|
||||
amount,
|
||||
gasLimit: gas,
|
||||
gasPrice,
|
||||
to,
|
||||
},
|
||||
} = props
|
||||
|
||||
const { txParams: { from, to: tokenAddress } } = txData
|
||||
|
||||
const tokenParams = {
|
||||
from: ethUtil.addHexPrefix(from),
|
||||
value: '0',
|
||||
gas: ethUtil.addHexPrefix(gas),
|
||||
gasPrice: ethUtil.addHexPrefix(gasPrice),
|
||||
}
|
||||
|
||||
const data = '0xa9059cbb' + Array.prototype.map.call(
|
||||
ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]),
|
||||
x => ('00' + x.toString(16)).slice(-2)
|
||||
).join('')
|
||||
|
||||
txData.txParams = {
|
||||
...tokenParams,
|
||||
to: ethUtil.addHexPrefix(tokenAddress),
|
||||
memo: memo && ethUtil.addHexPrefix(memo),
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
||||
return txData
|
||||
}
|
||||
|
@ -32,8 +32,9 @@ GasFeeDisplay.prototype.render = function () {
|
||||
})
|
||||
: h('div.currency-display', 'Loading...'),
|
||||
|
||||
h('div.send-v2__sliders-icon-container', {
|
||||
h('button.send-v2__sliders-icon-container', {
|
||||
onClick,
|
||||
disabled: !gasTotal,
|
||||
}, [
|
||||
h('i.fa.fa-sliders.send-v2__sliders-icon'),
|
||||
]),
|
||||
|
@ -20,6 +20,8 @@ const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
|
||||
multiplierBase: 16,
|
||||
})
|
||||
|
||||
const TOKEN_TRANSFER_FUNCTION_SIGNATURE = '0xa9059cbb'
|
||||
|
||||
module.exports = {
|
||||
MIN_GAS_PRICE_GWEI,
|
||||
MIN_GAS_PRICE_HEX,
|
||||
@ -27,4 +29,5 @@ module.exports = {
|
||||
MIN_GAS_LIMIT_HEX,
|
||||
MIN_GAS_LIMIT_DEC,
|
||||
MIN_GAS_TOTAL,
|
||||
TOKEN_TRANSFER_FUNCTION_SIGNATURE,
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ function mapStateToProps (state) {
|
||||
data,
|
||||
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
|
||||
tokenContract: getSelectedTokenContract(state),
|
||||
unapprovedTxs: state.metamask.unapprovedTxs,
|
||||
network: state.metamask.network,
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +71,7 @@ function mapDispatchToProps (dispatch) {
|
||||
),
|
||||
signTx: txParams => dispatch(actions.signTx(txParams)),
|
||||
updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)),
|
||||
updateTx: txData => dispatch(actions.updateTransaction(txData)),
|
||||
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
|
||||
addToAddressBook: address => dispatch(actions.addToAddressBook(address)),
|
||||
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
|
||||
@ -82,7 +85,6 @@ function mapDispatchToProps (dispatch) {
|
||||
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
|
||||
goHome: () => dispatch(actions.goHome()),
|
||||
clearSend: () => dispatch(actions.clearSend()),
|
||||
backToConfirmScreen: editingTransactionId => dispatch(actions.showConfTxPage({ id: editingTransactionId })),
|
||||
setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)),
|
||||
}
|
||||
}
|
||||
|
@ -1,308 +1,242 @@
|
||||
const PersistentForm = require('../../lib/persistent-form')
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const Component = require('react').Component
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../actions')
|
||||
const Qr = require('./qr-code')
|
||||
const isValidAddress = require('../util').isValidAddress
|
||||
module.exports = connect(mapStateToProps)(ShapeshiftForm)
|
||||
const classnames = require('classnames')
|
||||
const { qrcode } = require('qrcode-npm')
|
||||
const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions')
|
||||
const { isValidAddress } = require('../util')
|
||||
const SimpleDropdown = require('./dropdowns/simple-dropdown')
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const {
|
||||
coinOptions,
|
||||
tokenExchangeRates,
|
||||
selectedAddress,
|
||||
} = state.metamask
|
||||
|
||||
return {
|
||||
warning: state.appState.warning,
|
||||
isSubLoading: state.appState.isSubLoading,
|
||||
qrRequested: state.appState.qrRequested,
|
||||
coinOptions,
|
||||
tokenExchangeRates,
|
||||
selectedAddress,
|
||||
}
|
||||
}
|
||||
|
||||
inherits(ShapeshiftForm, PersistentForm)
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
shapeShiftSubview: () => dispatch(shapeShiftSubview()),
|
||||
pairUpdate: coin => dispatch(pairUpdate(coin)),
|
||||
buyWithShapeShift: data => dispatch(buyWithShapeShift(data)),
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ShapeshiftForm)
|
||||
|
||||
inherits(ShapeshiftForm, Component)
|
||||
function ShapeshiftForm () {
|
||||
PersistentForm.call(this)
|
||||
this.persistentFormParentId = 'shapeshift-buy-form'
|
||||
Component.call(this)
|
||||
|
||||
this.state = {
|
||||
depositCoin: 'btc',
|
||||
refundAddress: '',
|
||||
showQrCode: false,
|
||||
depositAddress: '',
|
||||
errorMessage: '',
|
||||
isLoading: false,
|
||||
bought: false,
|
||||
}
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.render = function () {
|
||||
return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain()
|
||||
ShapeshiftForm.prototype.componentWillMount = function () {
|
||||
this.props.shapeShiftSubview()
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.renderMain = function () {
|
||||
const marketinfo = this.props.buyView.formView.marketinfo
|
||||
const coinOptions = this.props.buyView.formView.coinOptions
|
||||
var coin = marketinfo.pair.split('_')[0].toUpperCase()
|
||||
|
||||
return h('.flex-column', {
|
||||
style: {
|
||||
position: 'relative',
|
||||
padding: '25px',
|
||||
paddingTop: '5px',
|
||||
width: '90%',
|
||||
minHeight: '215px',
|
||||
alignItems: 'center',
|
||||
overflowY: 'auto',
|
||||
},
|
||||
}, [
|
||||
h('.flex-row', {
|
||||
style: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'baseline',
|
||||
height: '42px',
|
||||
},
|
||||
}, [
|
||||
h('img', {
|
||||
src: coinOptions[coin].image,
|
||||
width: '25px',
|
||||
height: '25px',
|
||||
style: {
|
||||
marginRight: '5px',
|
||||
},
|
||||
}),
|
||||
|
||||
h('.input-container', {
|
||||
position: 'relative',
|
||||
}, [
|
||||
h('input#fromCoin.buy-inputs.ex-coins', {
|
||||
type: 'text',
|
||||
list: 'coinList',
|
||||
autoFocus: true,
|
||||
dataset: {
|
||||
persistentFormId: 'input-coin',
|
||||
},
|
||||
style: {
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
onChange: this.handleLiveInput.bind(this),
|
||||
defaultValue: 'BTC',
|
||||
}),
|
||||
|
||||
this.renderCoinList(),
|
||||
|
||||
h('i.fa.fa-pencil-square-o.edit-text', {
|
||||
style: {
|
||||
fontSize: '12px',
|
||||
color: '#F7861C',
|
||||
position: 'absolute',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
|
||||
h('.icon-control', {
|
||||
style: {
|
||||
position: 'relative',
|
||||
},
|
||||
}, [
|
||||
// Not visible on the screen, can't see it on master.
|
||||
|
||||
// h('i.fa.fa-refresh.fa-4.orange', {
|
||||
// style: {
|
||||
// bottom: '5px',
|
||||
// left: '5px',
|
||||
// color: '#F7861C',
|
||||
// },
|
||||
// onClick: this.updateCoin.bind(this),
|
||||
// }),
|
||||
h('i.fa.fa-chevron-right.fa-4.orange', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
bottom: '35%',
|
||||
left: '0%',
|
||||
color: '#F7861C',
|
||||
},
|
||||
onClick: this.updateCoin.bind(this),
|
||||
}),
|
||||
]),
|
||||
|
||||
h('#toCoin.ex-coins', marketinfo.pair.split('_')[1].toUpperCase()),
|
||||
|
||||
h('img', {
|
||||
src: coinOptions[marketinfo.pair.split('_')[1].toUpperCase()].image,
|
||||
width: '25px',
|
||||
height: '25px',
|
||||
style: {
|
||||
marginLeft: '5px',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
|
||||
h('.flex-column', {
|
||||
style: {
|
||||
marginTop: '1%',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
}, [
|
||||
this.props.warning ?
|
||||
this.props.warning &&
|
||||
h('span.error.flex-center', {
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
width: '229px',
|
||||
height: '82px',
|
||||
},
|
||||
}, this.props.warning)
|
||||
: this.renderInfo(),
|
||||
|
||||
this.renderRefundAddressForCoin(coin),
|
||||
]),
|
||||
|
||||
])
|
||||
ShapeshiftForm.prototype.onCoinChange = function (e) {
|
||||
const coin = e.target.value
|
||||
this.setState({
|
||||
depositCoin: coin,
|
||||
errorMessage: '',
|
||||
})
|
||||
this.props.pairUpdate(coin)
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.renderRefundAddressForCoin = function (coin) {
|
||||
return h(this.activeToggle('.input-container'), {
|
||||
style: {
|
||||
marginTop: '1%',
|
||||
},
|
||||
}, [
|
||||
ShapeshiftForm.prototype.onBuyWithShapeShift = function () {
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
showQrCode: true,
|
||||
})
|
||||
|
||||
h('div', `${coin} Address:`),
|
||||
|
||||
h('input#fromCoinAddress.buy-inputs', {
|
||||
type: 'text',
|
||||
placeholder: `Your ${coin} Refund Address`,
|
||||
dataset: {
|
||||
persistentFormId: 'refund-address',
|
||||
|
||||
},
|
||||
style: {
|
||||
boxSizing: 'border-box',
|
||||
width: '227px',
|
||||
height: '30px',
|
||||
padding: ' 5px ',
|
||||
},
|
||||
}),
|
||||
|
||||
h('i.fa.fa-pencil-square-o.edit-text', {
|
||||
style: {
|
||||
fontSize: '12px',
|
||||
color: '#F7861C',
|
||||
position: 'absolute',
|
||||
},
|
||||
}),
|
||||
h('div.flex-row', {
|
||||
style: {
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
}, [
|
||||
h('button', {
|
||||
onClick: this.shift.bind(this),
|
||||
style: {
|
||||
marginTop: '1%',
|
||||
},
|
||||
},
|
||||
'Submit'),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.shift = function () {
|
||||
var props = this.props
|
||||
var withdrawal = this.props.buyView.buyAddress
|
||||
var returnAddress = document.getElementById('fromCoinAddress').value
|
||||
var pair = this.props.buyView.formView.marketinfo.pair
|
||||
var data = {
|
||||
'withdrawal': withdrawal,
|
||||
'pair': pair,
|
||||
'returnAddress': returnAddress,
|
||||
const {
|
||||
buyWithShapeShift,
|
||||
selectedAddress: withdrawal,
|
||||
} = this.props
|
||||
const {
|
||||
refundAddress: returnAddress,
|
||||
depositCoin,
|
||||
} = this.state
|
||||
const pair = `${depositCoin}_eth`
|
||||
const data = {
|
||||
withdrawal,
|
||||
pair,
|
||||
returnAddress,
|
||||
// Public api key
|
||||
'apiKey': '803d1f5df2ed1b1476e4b9e6bcd089e34d8874595dda6a23b67d93c56ea9cc2445e98a6748b219b2b6ad654d9f075f1f1db139abfa93158c04e825db122c14b6',
|
||||
}
|
||||
var message = [
|
||||
`Deposit Limit: ${props.buyView.formView.marketinfo.limit}`,
|
||||
`Deposit Minimum:${props.buyView.formView.marketinfo.minimum}`,
|
||||
]
|
||||
|
||||
if (isValidAddress(withdrawal)) {
|
||||
this.props.dispatch(actions.coinShiftRquest(data, message))
|
||||
buyWithShapeShift(data)
|
||||
.then(d => this.setState({
|
||||
showQrCode: true,
|
||||
depositAddress: d.deposit,
|
||||
isLoading: false,
|
||||
}))
|
||||
.catch(() => this.setState({
|
||||
showQrCode: false,
|
||||
errorMessage: 'Invalid Request',
|
||||
isLoading: false,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.renderCoinList = function () {
|
||||
var list = Object.keys(this.props.buyView.formView.coinOptions).map((item) => {
|
||||
return h('option', {
|
||||
value: item,
|
||||
}, item)
|
||||
})
|
||||
ShapeshiftForm.prototype.renderMetadata = function (label, value) {
|
||||
return h('div', {className: 'shapeshift-form__metadata-wrapper'}, [
|
||||
|
||||
return h('datalist#coinList', {
|
||||
onClick: (event) => {
|
||||
event.preventDefault()
|
||||
},
|
||||
}, list)
|
||||
}
|
||||
h('div.shapeshift-form__metadata-label', {}, [
|
||||
h('span', `${label}:`),
|
||||
]),
|
||||
|
||||
ShapeshiftForm.prototype.updateCoin = function (event) {
|
||||
event.preventDefault()
|
||||
const props = this.props
|
||||
var coinOptions = this.props.buyView.formView.coinOptions
|
||||
var coin = document.getElementById('fromCoin').value
|
||||
h('div.shapeshift-form__metadata-value', {}, [
|
||||
h('span', value),
|
||||
]),
|
||||
|
||||
if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') {
|
||||
var message = 'Not a valid coin'
|
||||
return props.dispatch(actions.displayWarning(message))
|
||||
} else {
|
||||
return props.dispatch(actions.pairUpdate(coin))
|
||||
}
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.handleLiveInput = function () {
|
||||
const props = this.props
|
||||
var coinOptions = this.props.buyView.formView.coinOptions
|
||||
var coin = document.getElementById('fromCoin').value
|
||||
|
||||
if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') {
|
||||
return null
|
||||
} else {
|
||||
return props.dispatch(actions.pairUpdate(coin))
|
||||
}
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.renderInfo = function () {
|
||||
const marketinfo = this.props.buyView.formView.marketinfo
|
||||
const coinOptions = this.props.buyView.formView.coinOptions
|
||||
var coin = marketinfo.pair.split('_')[0].toUpperCase()
|
||||
|
||||
return h('span', {
|
||||
style: {
|
||||
},
|
||||
}, [
|
||||
h('h3.flex-row.text-transform-uppercase', {
|
||||
style: {
|
||||
color: '#868686',
|
||||
paddingTop: '4px',
|
||||
justifyContent: 'space-around',
|
||||
textAlign: 'center',
|
||||
fontSize: '17px',
|
||||
},
|
||||
}, `Market Info for ${marketinfo.pair.replace('_', ' to ').toUpperCase()}:`),
|
||||
h('.marketinfo', ['Status : ', `${coinOptions[coin].status}`]),
|
||||
h('.marketinfo', ['Exchange Rate: ', `${marketinfo.rate}`]),
|
||||
h('.marketinfo', ['Limit: ', `${marketinfo.limit}`]),
|
||||
h('.marketinfo', ['Minimum : ', `${marketinfo.minimum}`]),
|
||||
])
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.activeToggle = function (elementType) {
|
||||
if (!this.props.buyView.formView.response || this.props.warning) return elementType
|
||||
return `${elementType}.inactive`
|
||||
ShapeshiftForm.prototype.renderMarketInfo = function () {
|
||||
const { depositCoin } = this.state
|
||||
const coinPair = `${depositCoin}_eth`
|
||||
const { tokenExchangeRates } = this.props
|
||||
const {
|
||||
limit,
|
||||
rate,
|
||||
minimum,
|
||||
} = tokenExchangeRates[coinPair] || {}
|
||||
|
||||
return h('div.shapeshift-form__metadata', {}, [
|
||||
|
||||
this.renderMetadata('Status', limit ? 'Available' : 'Unavailable'),
|
||||
this.renderMetadata('Limit', limit),
|
||||
this.renderMetadata('Exchange Rate', rate),
|
||||
this.renderMetadata('Minimum', minimum),
|
||||
|
||||
])
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.renderLoading = function () {
|
||||
return h('span', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: '70px',
|
||||
bottom: '194px',
|
||||
background: 'transparent',
|
||||
width: '229px',
|
||||
height: '82px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}, [
|
||||
h('img', {
|
||||
style: {
|
||||
width: '60px',
|
||||
},
|
||||
ShapeshiftForm.prototype.renderQrCode = function () {
|
||||
const { depositAddress, isLoading } = this.state
|
||||
const qrImage = qrcode(4, 'M')
|
||||
qrImage.addData(depositAddress)
|
||||
qrImage.make()
|
||||
|
||||
return h('div.shapeshift-form', {}, [
|
||||
|
||||
h('div.shapeshift-form__deposit-instruction', [
|
||||
'Deposit your BTC to the address below:',
|
||||
]),
|
||||
|
||||
h('div', depositAddress),
|
||||
|
||||
h('div.shapeshift-form__qr-code', [
|
||||
isLoading
|
||||
? h('img', {
|
||||
src: 'images/loading.svg',
|
||||
style: { width: '60px'},
|
||||
})
|
||||
: h('div', {
|
||||
dangerouslySetInnerHTML: { __html: qrImage.createTableTag(4) },
|
||||
}),
|
||||
]),
|
||||
|
||||
this.renderMarketInfo(),
|
||||
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
ShapeshiftForm.prototype.render = function () {
|
||||
const { coinOptions, btnClass } = this.props
|
||||
const { depositCoin, errorMessage, showQrCode, depositAddress } = this.state
|
||||
const coinPair = `${depositCoin}_eth`
|
||||
const { tokenExchangeRates } = this.props
|
||||
const token = tokenExchangeRates[coinPair]
|
||||
|
||||
return h('div.shapeshift-form-wrapper', [
|
||||
showQrCode
|
||||
? this.renderQrCode()
|
||||
: h('div.shapeshift-form', [
|
||||
h('div.shapeshift-form__selectors', [
|
||||
|
||||
h('div.shapeshift-form__selector', [
|
||||
|
||||
h('div.shapeshift-form__selector-label', 'Deposit'),
|
||||
|
||||
h(SimpleDropdown, {
|
||||
selectedOption: this.state.depositCoin,
|
||||
onSelect: this.onCoinChange,
|
||||
options: Object.entries(coinOptions).map(([coin]) => ({
|
||||
value: coin.toLowerCase(),
|
||||
displayValue: coin,
|
||||
})),
|
||||
}),
|
||||
|
||||
]),
|
||||
|
||||
h('div.icon.shapeshift-form__caret', {
|
||||
style: { backgroundImage: 'url(images/caret-right.svg)'},
|
||||
}),
|
||||
|
||||
h('div.shapeshift-form__selector', [
|
||||
|
||||
h('div.shapeshift-form__selector-label', [
|
||||
'Receive',
|
||||
]),
|
||||
|
||||
h('div.shapeshift-form__selector-input', ['ETH']),
|
||||
|
||||
]),
|
||||
|
||||
]),
|
||||
|
||||
h('div', {
|
||||
className: classnames('shapeshift-form__address-input-wrapper', {
|
||||
'shapeshift-form__address-input-wrapper--error': errorMessage,
|
||||
}),
|
||||
}, [
|
||||
|
||||
h('div.shapeshift-form__address-input-label', [
|
||||
'Your Refund Address',
|
||||
]),
|
||||
|
||||
h('input.shapeshift-form__address-input', {
|
||||
type: 'text',
|
||||
onChange: e => this.setState({
|
||||
refundAddress: e.target.value,
|
||||
errorMessage: '',
|
||||
}),
|
||||
}),
|
||||
|
||||
h('divshapeshift-form__address-input-error-message', [errorMessage]),
|
||||
]),
|
||||
|
||||
this.renderMarketInfo(),
|
||||
|
||||
]),
|
||||
|
||||
!depositAddress && h('button.shapeshift-form__shapeshift-buy-btn', {
|
||||
className: btnClass,
|
||||
disabled: !token,
|
||||
onClick: () => this.onBuyWithShapeShift(),
|
||||
}, ['Buy']),
|
||||
|
||||
])
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ module.exports = connect(mapStateToProps)(ShiftListItem)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
selectedAddress: state.metamask.selectedAddress,
|
||||
conversionRate: state.metamask.conversionRate,
|
||||
currentCurrency: state.metamask.currentCurrency,
|
||||
}
|
||||
@ -28,8 +29,10 @@ function ShiftListItem () {
|
||||
}
|
||||
|
||||
ShiftListItem.prototype.render = function () {
|
||||
const { selectedAddress, receivingAddress } = this.props
|
||||
return (
|
||||
h('div.tx-list-item.tx-list-clickable', {
|
||||
selectedAddress === receivingAddress
|
||||
? h('div.tx-list-item.tx-list-clickable', {
|
||||
style: {
|
||||
paddingTop: '20px',
|
||||
paddingBottom: '20px',
|
||||
@ -58,6 +61,7 @@ ShiftListItem.prototype.render = function () {
|
||||
this.renderInfo(),
|
||||
this.renderUtilComponents(),
|
||||
])
|
||||
: null
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,9 @@ TokenCell.prototype.render = function () {
|
||||
numberOfDecimals: 2,
|
||||
conversionRate: currentTokenToFiatRate,
|
||||
})
|
||||
formattedFiat = `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
|
||||
formattedFiat = currentTokenInFiat.toString() === '0'
|
||||
? ''
|
||||
: `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
|
||||
}
|
||||
|
||||
const showFiat = Boolean(currentTokenInFiat) && currentCurrency.toUpperCase() !== symbol
|
||||
@ -104,7 +106,7 @@ TokenCell.prototype.render = function () {
|
||||
|
||||
h(Identicon, {
|
||||
className: 'token-list-item__identicon',
|
||||
diameter: 45,
|
||||
diameter: 50,
|
||||
address,
|
||||
network,
|
||||
}),
|
||||
|
@ -1,6 +1,7 @@
|
||||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
|
||||
const EthBalance = require('./eth-balance')
|
||||
const addressSummary = require('../util').addressSummary
|
||||
@ -9,18 +10,33 @@ const CopyButton = require('./copyButton')
|
||||
const vreme = new (require('vreme'))()
|
||||
const Tooltip = require('./tooltip')
|
||||
const numberToBN = require('number-to-bn')
|
||||
const actions = require('../actions')
|
||||
|
||||
const TransactionIcon = require('./transaction-list-item-icon')
|
||||
const ShiftListItem = require('./shift-list-item')
|
||||
module.exports = TransactionListItem
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(null, mapDispatchToProps)(TransactionListItem)
|
||||
|
||||
inherits(TransactionListItem, Component)
|
||||
function TransactionListItem () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
TransactionListItem.prototype.showRetryButton = function () {
|
||||
const { transaction = {} } = this.props
|
||||
const { status, time } = transaction
|
||||
return status === 'submitted' && Date.now() - time > 30000
|
||||
}
|
||||
|
||||
TransactionListItem.prototype.render = function () {
|
||||
const { transaction, network, conversionRate, currentCurrency } = this.props
|
||||
const { status } = transaction
|
||||
if (transaction.key === 'shapeshift') {
|
||||
if (network === '1') return h(ShiftListItem, transaction)
|
||||
}
|
||||
@ -32,7 +48,7 @@ TransactionListItem.prototype.render = function () {
|
||||
|
||||
var isMsg = ('msgParams' in transaction)
|
||||
var isTx = ('txParams' in transaction)
|
||||
var isPending = transaction.status === 'unapproved'
|
||||
var isPending = status === 'unapproved'
|
||||
let txParams
|
||||
if (isTx) {
|
||||
txParams = transaction.txParams
|
||||
@ -44,7 +60,7 @@ TransactionListItem.prototype.render = function () {
|
||||
|
||||
const isClickable = ('hash' in transaction && isLinkable) || isPending
|
||||
return (
|
||||
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
||||
h('.transaction-list-item.flex-column', {
|
||||
onClick: (event) => {
|
||||
if (isPending) {
|
||||
this.props.showTx(transaction.id)
|
||||
@ -56,9 +72,14 @@ TransactionListItem.prototype.render = function () {
|
||||
},
|
||||
style: {
|
||||
padding: '20px 0',
|
||||
alignItems: 'center',
|
||||
},
|
||||
}, [
|
||||
h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
}, [
|
||||
|
||||
h('.identicon-wrapper.flex-column.flex-center.select-none', [
|
||||
h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
|
||||
]),
|
||||
@ -97,10 +118,46 @@ TransactionListItem.prototype.render = function () {
|
||||
showFiat: false,
|
||||
style: {fontSize: '15px'},
|
||||
}) : h('.flex-column'),
|
||||
]),
|
||||
|
||||
this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', {
|
||||
onClick: event => {
|
||||
event.stopPropagation()
|
||||
this.resubmit()
|
||||
},
|
||||
style: {
|
||||
height: '22px',
|
||||
borderRadius: '22px',
|
||||
color: '#F9881B',
|
||||
padding: '0 20px',
|
||||
backgroundColor: '#FFE3C9',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
fontSize: '8px',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
}, [
|
||||
h('div', {
|
||||
style: {
|
||||
paddingRight: '2px',
|
||||
},
|
||||
}, 'Taking too long?'),
|
||||
h('div', {
|
||||
style: {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
}, 'Retry with a higher gas price here'),
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
TransactionListItem.prototype.resubmit = function () {
|
||||
const { transaction } = this.props
|
||||
this.props.retryTransaction(transaction.id)
|
||||
}
|
||||
|
||||
function domainField (txParams) {
|
||||
return h('div', {
|
||||
style: {
|
||||
|
@ -170,6 +170,7 @@ TxListItem.prototype.getSendTokenTotal = async function () {
|
||||
TxListItem.prototype.render = function () {
|
||||
const {
|
||||
transactionStatus,
|
||||
transactionAmount,
|
||||
onClick,
|
||||
transActionId,
|
||||
dateString,
|
||||
@ -177,6 +178,7 @@ TxListItem.prototype.render = function () {
|
||||
className,
|
||||
} = this.props
|
||||
const { total, fiatTotal } = this.state
|
||||
const showFiatTotal = transactionAmount !== '0x0' && fiatTotal
|
||||
|
||||
return h(`div${className || ''}`, {
|
||||
key: transActionId,
|
||||
@ -232,13 +234,9 @@ TxListItem.prototype.render = function () {
|
||||
style: {},
|
||||
}, [
|
||||
|
||||
h('span', {
|
||||
className: classnames('tx-list-value', {
|
||||
'tx-list-value--confirmed': transactionStatus === 'confirmed',
|
||||
}),
|
||||
}, total),
|
||||
h('span.tx-list-value', total),
|
||||
|
||||
fiatTotal && h('span.tx-list-fiat-value', fiatTotal),
|
||||
showFiatTotal && h('span.tx-list-fiat-value', fiatTotal),
|
||||
|
||||
]),
|
||||
]),
|
||||
|
@ -42,23 +42,22 @@ TxList.prototype.componentWillMount = function () {
|
||||
}
|
||||
|
||||
TxList.prototype.render = function () {
|
||||
return h('div.flex-column.tx-list-container', {}, [
|
||||
|
||||
return h('div.flex-column', [
|
||||
h('div.flex-row.tx-list-header-wrapper', [
|
||||
h('div.flex-row.tx-list-header', [
|
||||
h('div', 'transactions'),
|
||||
]),
|
||||
]),
|
||||
|
||||
h('div.flex-column.tx-list-container', {}, [
|
||||
this.renderTransaction(),
|
||||
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
TxList.prototype.renderTransaction = function () {
|
||||
const { txsToRender, conversionRate } = this.props
|
||||
return txsToRender.length
|
||||
? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate))
|
||||
? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate, i))
|
||||
: [h(
|
||||
'div.tx-list-item.tx-list-item--empty',
|
||||
{ key: 'tx-list-none' },
|
||||
@ -67,12 +66,16 @@ TxList.prototype.renderTransaction = function () {
|
||||
}
|
||||
|
||||
// TODO: Consider moving TxListItem into a separate component
|
||||
TxList.prototype.renderTransactionListItem = function (transaction, conversionRate) {
|
||||
TxList.prototype.renderTransactionListItem = function (transaction, conversionRate, index) {
|
||||
// console.log({transaction})
|
||||
// refer to transaction-list.js:line 58
|
||||
|
||||
if (transaction.key === 'shapeshift') {
|
||||
return h(ShiftListItem, transaction)
|
||||
return h('div', {
|
||||
key: `shapeshift${index}`,
|
||||
}, [
|
||||
h(ShiftListItem, transaction),
|
||||
])
|
||||
}
|
||||
|
||||
const props = {
|
||||
|
@ -74,18 +74,14 @@ TxView.prototype.renderButtons = function () {
|
||||
return !selectedToken
|
||||
? (
|
||||
h('div.flex-row.flex-center.hero-balance-buttons', [
|
||||
h('button.btn-clear', {
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
h('button.btn-clear.hero-balance-button', {
|
||||
onClick: () => showModal({
|
||||
name: 'BUY',
|
||||
name: 'DEPOSIT_ETHER',
|
||||
}),
|
||||
}, 'DEPOSIT'),
|
||||
|
||||
h('button.btn-clear', {
|
||||
h('button.btn-clear.hero-balance-button', {
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
marginLeft: '0.8em',
|
||||
},
|
||||
onClick: () => history.push(SEND_ROUTE),
|
||||
@ -94,11 +90,7 @@ TxView.prototype.renderButtons = function () {
|
||||
)
|
||||
: (
|
||||
h('div.flex-row.flex-center.hero-balance-buttons', [
|
||||
h('button.btn-clear', {
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
marginLeft: '0.8em',
|
||||
},
|
||||
h('button.btn-clear.hero-balance-button', {
|
||||
onClick: () => history.push(SEND_ROUTE),
|
||||
}, 'SEND'),
|
||||
])
|
||||
@ -114,7 +106,7 @@ TxView.prototype.render = function () {
|
||||
|
||||
h('div.flex-row.phone-visible', {
|
||||
style: {
|
||||
margin: '1em 0.9em',
|
||||
margin: '1.5em 1.2em 0',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
@ -150,7 +142,7 @@ TxView.prototype.render = function () {
|
||||
|
||||
!isMascara && h('div.open-in-browser', {
|
||||
onClick: () => global.platform.openExtensionInBrowser(),
|
||||
}, [h('img', { src: 'images/open.svg' })]),
|
||||
}, [h('img', { src: 'images/popout.svg' })]),
|
||||
|
||||
]),
|
||||
|
||||
|
@ -136,7 +136,7 @@ WalletView.prototype.render = function () {
|
||||
selectedIdentity.name,
|
||||
]),
|
||||
|
||||
h('button.wallet-view__details-button', 'DETAILS'),
|
||||
h('button.btn-clear.wallet-view__details-button', 'DETAILS'),
|
||||
]),
|
||||
]),
|
||||
|
||||
@ -157,7 +157,7 @@ WalletView.prototype.render = function () {
|
||||
|
||||
h(TokenList),
|
||||
|
||||
h('button.wallet-view__add-token-button', {
|
||||
h('button.btn-clear.wallet-view__add-token-button', {
|
||||
onClick: () => history.push(ADD_TOKEN_ROUTE),
|
||||
}, 'Add Token'),
|
||||
])
|
||||
|
@ -94,6 +94,7 @@
|
||||
padding: 12px 0;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, .05);
|
||||
@ -164,9 +165,18 @@
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
flex-flow: row nowrap;
|
||||
margin: 30px 0 51px;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__button {
|
||||
flex: 1 0 141px;
|
||||
margin: 0 12px;
|
||||
padding: 10px 22px;
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
&__token-icons-container {
|
||||
@ -324,18 +334,10 @@
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
flex-flow: row nowrap;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px 0;
|
||||
margin: 0;
|
||||
border-top: 1px solid $gallery;
|
||||
|
||||
button {
|
||||
flex: 1 0 auto;
|
||||
margin: 0 12px;
|
||||
}
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,43 @@
|
||||
background-color: #02c9b1; // TODO: reusable color in colors.css
|
||||
}
|
||||
|
||||
button.btn-clear {
|
||||
.btn-clear {
|
||||
background: $white;
|
||||
border: 1px solid;
|
||||
text-align: center;
|
||||
padding: .8rem 1rem;
|
||||
color: $curious-blue;
|
||||
border: 2px solid $spindle;
|
||||
border-radius: 4px;
|
||||
font-size: .85rem;
|
||||
font-weight: 400;
|
||||
transition: border-color .3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
cursor: auto;
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
background: $white;
|
||||
text-align: center;
|
||||
padding: .9rem 1rem;
|
||||
color: $scorpion;
|
||||
border: 2px solid $dusty-gray;
|
||||
border-radius: 4px;
|
||||
font-size: .85rem;
|
||||
font-weight: 400;
|
||||
transition: border-color .3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: $scorpion;
|
||||
}
|
||||
}
|
||||
|
||||
// No longer used in flat design, remove when modal buttons done
|
||||
|
@ -2,13 +2,15 @@
|
||||
position: relative;
|
||||
align-items: center;
|
||||
font-family: Roboto;
|
||||
flex: 0 0 auto;
|
||||
flex: 1 0 auto;
|
||||
flex-flow: column nowrap;
|
||||
box-shadow: 0 2px 4px 0 rgba($black, .08);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
width: 100%;
|
||||
box-shadow: initial;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 576px) {
|
||||
@ -102,15 +104,10 @@
|
||||
|
||||
.confirm-screen-back-button {
|
||||
background: transparent;
|
||||
border: 1px solid $curious-blue;
|
||||
left: 24px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
color: $curious-blue;
|
||||
padding: 6px 13px 7px 12px;
|
||||
border-radius: 2px;
|
||||
height: 30px;
|
||||
width: 54px;
|
||||
padding: 6px 12px;
|
||||
font-size: .7rem;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
margin-right: 12px;
|
||||
@ -277,8 +274,8 @@ section .confirm-screen-account-number,
|
||||
}
|
||||
|
||||
.confirm-screen-confirm-button {
|
||||
height: 62px;
|
||||
border-radius: 2px;
|
||||
height: 50px;
|
||||
border-radius: 4px;
|
||||
background-color: #02c9b1;
|
||||
font-size: 16px;
|
||||
color: $white;
|
||||
@ -290,11 +287,11 @@ section .confirm-screen-account-number,
|
||||
box-shadow: none;
|
||||
flex: 1 0 auto;
|
||||
font-weight: 300;
|
||||
margin: 0 8px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.btn-light.confirm-screen-cancel-button {
|
||||
height: 62px;
|
||||
height: 50px;
|
||||
background: none;
|
||||
border: none;
|
||||
opacity: 1;
|
||||
@ -303,12 +300,11 @@ section .confirm-screen-account-number,
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
flex: 1 0 auto;
|
||||
font-weight: 300;
|
||||
margin: 0 8px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
#pending-tx-form {
|
||||
@ -317,7 +313,7 @@ section .confirm-screen-account-number,
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
background-color: $white;
|
||||
padding: 12px 18px;
|
||||
padding: 12px;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
width: 100%;
|
||||
|
@ -17,7 +17,16 @@
|
||||
@media screen and (min-width: 576px) {
|
||||
height: 75px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.metafox-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.app-header--initialized {
|
||||
|
||||
@media screen and (min-width: 576px) {
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@ -27,10 +36,6 @@
|
||||
bottom: -32px;
|
||||
}
|
||||
}
|
||||
|
||||
.metafox-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.app-header-contents {
|
||||
@ -53,7 +58,7 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1281px) {
|
||||
width: 65vw;
|
||||
width: 62vw;
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,8 +66,10 @@
|
||||
font-family: Roboto;
|
||||
text-transform: uppercase;
|
||||
font-weight: 400;
|
||||
color: #22232c; // $shark
|
||||
line-height: 29px;
|
||||
font-size: 1.1rem;
|
||||
position: relative;
|
||||
padding-left: 15px;
|
||||
color: #5b5d67;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
display: none;
|
||||
|
@ -16,7 +16,8 @@
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin: 2.8em 2.37em .8em;
|
||||
margin: 2.3em 2.37em .8em;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.balance-container {
|
||||
@ -37,13 +38,16 @@
|
||||
}
|
||||
|
||||
.balance-display {
|
||||
.token-amount {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
text-align: center;
|
||||
|
||||
.token-amount {
|
||||
font-size: 175%;
|
||||
margin-top: 12.5%;
|
||||
font-size: 1.75rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.fiat-amount {
|
||||
@ -54,12 +58,12 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: $break-large) {
|
||||
margin-left: 3%;
|
||||
margin-left: .8em;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
|
||||
.token-amount {
|
||||
font-size: 135%;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.fiat-amount {
|
||||
@ -69,13 +73,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.balance-icon {
|
||||
border-radius: 25px;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border: 1px solid $alto;
|
||||
}
|
||||
|
||||
.hero-balance-buttons {
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
@ -89,26 +86,9 @@
|
||||
flex-grow: 2;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
button.btn-clear {
|
||||
background: $white;
|
||||
border: 1px solid;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
border-color: $curious-blue;
|
||||
color: $curious-blue;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $break-large) {
|
||||
border-color: $curious-blue;
|
||||
color: $curious-blue;
|
||||
padding: 0;
|
||||
width: 85px;
|
||||
height: 34px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hero-balance-button {
|
||||
width: 6rem;
|
||||
}
|
||||
|
@ -53,3 +53,5 @@
|
||||
@import './editable-label.scss';
|
||||
|
||||
@import './pages/index.scss';
|
||||
|
||||
@import './new-account.scss';
|
||||
|
@ -258,19 +258,10 @@
|
||||
width: 286px;
|
||||
}
|
||||
|
||||
.btn-clear {
|
||||
min-height: 28px;
|
||||
font-size: 14px;
|
||||
border-color: $curious-blue;
|
||||
color: $curious-blue;
|
||||
border-radius: 2px;
|
||||
flex-basis: 100%;
|
||||
width: 75%;
|
||||
.account-modal__button {
|
||||
margin-top: 17px;
|
||||
padding: 10px 22px;
|
||||
height: 44px;
|
||||
width: 235px;
|
||||
font-family: Roboto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,17 +337,17 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-clear {
|
||||
.export-private-key__button {
|
||||
margin-top: 17px;
|
||||
padding: 10px 22px;
|
||||
width: 141px;
|
||||
height: 54px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
.export-private-key__button--cancel {
|
||||
margin-right: 15px;
|
||||
border-color: $dusty-gray;
|
||||
color: $scorpion;
|
||||
}
|
||||
}
|
||||
|
||||
.private-key-password-display-wrapper {
|
||||
@ -495,10 +486,9 @@
|
||||
|
||||
.hide-token-confirmation {
|
||||
min-height: 250.72px;
|
||||
width: 374.49px;
|
||||
border-radius: 4px;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 1px 7px 0 rgba(0,0,0,0.5);
|
||||
background-color: $white;
|
||||
box-shadow: 0 1px 7px 0 rgba(0, 0, 0, .5);
|
||||
|
||||
&__container {
|
||||
padding: 24px 27px 21px;
|
||||
@ -508,7 +498,7 @@
|
||||
}
|
||||
|
||||
&__identicon {
|
||||
margin-bottom: 10px
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&__symbol {
|
||||
@ -547,20 +537,11 @@
|
||||
justify-content: center;
|
||||
margin-top: 15px;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
height: 44px;
|
||||
width: 113px;
|
||||
border: 1px solid $scorpion;
|
||||
border-radius: 2px;
|
||||
color: $tundora;
|
||||
font-family: Roboto;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
width: 141px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,3 +580,256 @@
|
||||
font-size: 17px;
|
||||
color: $nile-blue;
|
||||
}
|
||||
|
||||
// Deposit Ether Modal
|
||||
.deposit-ether-modal {
|
||||
border-radius: 8px;
|
||||
font-family: Roboto;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
|
||||
&__header {
|
||||
width: 100%;
|
||||
border-radius: 8px 8px 0 0;
|
||||
background-color: $mid-gray;
|
||||
display: flex;
|
||||
position: relative;
|
||||
padding: 25px;
|
||||
flex-flow: column;
|
||||
align-items: flex-start;
|
||||
|
||||
&__title {
|
||||
color: $white;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&__description {
|
||||
color: $white;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&__close::after {
|
||||
content: '\00D7';
|
||||
font-size: 2em;
|
||||
color: $white;
|
||||
position: absolute;
|
||||
top: 20.8px;
|
||||
right: 28px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__buy-rows {
|
||||
width: 100%;
|
||||
padding: 33px;
|
||||
padding-top: 0px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__buy-row {
|
||||
border-bottom: 1px solid $alto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
padding-bottom: 25px;
|
||||
padding-top: 25px;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
min-height: 360px;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
padding-top: 45px;
|
||||
}
|
||||
|
||||
&__back {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
&__shapeshift-buy {
|
||||
padding-top: 25px;
|
||||
position: relative;
|
||||
@media screen and (max-width: 575px) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
padding-bottom: 25px;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
padding-top: 20px;
|
||||
min-height: 240px;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 0.3 1 auto;
|
||||
|
||||
@media screen and (min-width: 575px) {
|
||||
min-width: 215px;
|
||||
}
|
||||
}
|
||||
|
||||
&__coinbase-logo {
|
||||
height: 40px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
&__shapeshift-logo {
|
||||
height: 60px;
|
||||
width: 174px;
|
||||
}
|
||||
|
||||
&__eth-logo {
|
||||
border-radius: 50%;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
border: 3px solid $tundora;
|
||||
z-index: 25;
|
||||
padding: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&__right {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__description {
|
||||
color: $cape-cod;
|
||||
flex: 0.5 1 auto;
|
||||
|
||||
@media screen and (min-width: 575px) {
|
||||
min-width: 315px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-top: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@media screen and (min-width: 575px) {
|
||||
min-width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__buy-row:last-of-type {
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
&__deposit-button, .shapeshift-form__shapeshift-buy-btn {
|
||||
height: 54px;
|
||||
width: 257px;
|
||||
border: 1px solid $curious-blue;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: $curious-blue;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
.shapeshift-form-wrapper {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 28px;
|
||||
flex: 1 0 auto;
|
||||
|
||||
.shapeshift-form {
|
||||
width: auto;
|
||||
|
||||
&__caret {
|
||||
width: auto;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shapeshift-form__shapeshift-buy-btn {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.simple-dropdown {
|
||||
color: #5B5D67;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
line-height: 21px;
|
||||
border: 1px solid #D8D8D8;
|
||||
background-color: #FFFFFF;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
line-height: 44px;
|
||||
font-family: Montserrat Light;
|
||||
}
|
||||
|
||||
.simple-dropdown__selected {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
//Notification Modal
|
||||
|
||||
.notification-modal-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
border: 1px solid $alto;
|
||||
box-shadow: 0 0 2px 2px $alto;
|
||||
font-family: Roboto;
|
||||
}
|
||||
|
||||
.notification-modal-header {
|
||||
background: $wild-sand;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 30px;
|
||||
font-size: 22px;
|
||||
color: $nile-blue;
|
||||
height: 79px;
|
||||
}
|
||||
|
||||
.notification-modal-message {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.notification-modal-message {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 17px;
|
||||
color: $nile-blue;
|
||||
}
|
||||
|
@ -8,42 +8,26 @@
|
||||
}
|
||||
|
||||
.network-component.pointer {
|
||||
border: 1px solid $shark;
|
||||
border: 2px solid $silver;
|
||||
border-radius: 82px;
|
||||
padding: 6px;
|
||||
padding: 3px;
|
||||
flex: 0 0 auto;
|
||||
|
||||
&.ethereum-network {
|
||||
border-color: rgb(3, 135, 137);
|
||||
|
||||
.menu-icon-circle div {
|
||||
&.ethereum-network .menu-icon-circle div {
|
||||
background-color: rgba(3, 135, 137, .7) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.ropsten-test-network {
|
||||
border-color: rgb(233, 21, 80);
|
||||
|
||||
.menu-icon-circle div {
|
||||
&.ropsten-test-network .menu-icon-circle div {
|
||||
background-color: rgba(233, 21, 80, .7) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.kovan-test-network {
|
||||
border-color: rgb(105, 4, 150);
|
||||
|
||||
.menu-icon-circle div {
|
||||
&.kovan-test-network .menu-icon-circle div {
|
||||
background-color: rgba(105, 4, 150, .7) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.rinkeby-test-network {
|
||||
border-color: rgb(235, 179, 63);
|
||||
|
||||
.menu-icon-circle div {
|
||||
&.rinkeby-test-network .menu-icon-circle div {
|
||||
background-color: rgba(235, 179, 63, .7) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-item {
|
||||
@ -66,11 +50,12 @@
|
||||
}
|
||||
|
||||
.network-name {
|
||||
line-height: 15px;
|
||||
padding: 0 4px;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
flex: 1 0 auto;
|
||||
color: $tundora;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.network-droppo {
|
||||
@ -167,3 +152,6 @@
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.network-caret {
|
||||
margin: 0 8px 2px;
|
||||
}
|
||||
|
192
ui/app/css/itcss/components/new-account.scss
Normal file
192
ui/app/css/itcss/components/new-account.scss
Normal file
@ -0,0 +1,192 @@
|
||||
.new-account {
|
||||
width: 376px;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 0 7px 0 rgba(0,0,0,0.08);
|
||||
z-index: 25;
|
||||
padding-bottom: 31px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
border-bottom: 1px solid $geyser;
|
||||
}
|
||||
|
||||
&__title {
|
||||
color: $tundora;
|
||||
font-family: Roboto;
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
line-height: 43px;
|
||||
margin-top: 22px;
|
||||
margin-left: 29px;
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
margin-left: 22px;
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
|
||||
&__tab {
|
||||
height: 54px;
|
||||
width: 75px;
|
||||
padding: 15px 10px;
|
||||
color: $dusty-gray;
|
||||
font-family: Roboto;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__tab:first-of-type {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
&__unselected:hover {
|
||||
color: $black;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&__selected {
|
||||
color: $curious-blue;
|
||||
border-bottom: 3px solid $curious-blue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.new-account-import-form {
|
||||
&__select-section {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
margin-top: 29px;
|
||||
}
|
||||
|
||||
&__select-label {
|
||||
color: $scorpion;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
&__select {
|
||||
height: 54px;
|
||||
width: 210px;
|
||||
border: 1px solid #D2D8DD;
|
||||
border-radius: 4px;
|
||||
background-color: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.Select-control,
|
||||
.Select-control:hover {
|
||||
height: 100%;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
|
||||
.Select-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__instruction {
|
||||
color: $scorpion;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
align-self: flex-start;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
&__private-key {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
margin-top: 34px;
|
||||
}
|
||||
|
||||
&__input-password {
|
||||
height: 54px;
|
||||
width: 315px;
|
||||
border: 1px solid $geyser;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
margin-top: 16px;
|
||||
color: $scorpion;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
|
||||
&__json {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
margin-top: 29px;
|
||||
}
|
||||
}
|
||||
|
||||
.new-account-create-form {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
|
||||
&__input-label {
|
||||
color: $scorpion;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
margin-top: 29px;
|
||||
align-self: flex-start;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
&__input {
|
||||
height: 54px;
|
||||
width: 315.84px;
|
||||
border: 1px solid $geyser;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
color: $scorpion;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
margin-top: 15px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
margin-top: 39px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
&__button-cancel,
|
||||
&__button-create {
|
||||
height: 55px;
|
||||
width: 150px;
|
||||
border-radius: 2px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
&__button-cancel {
|
||||
border: 1px solid $dusty-gray;
|
||||
color: $dusty-gray;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__button-create {
|
||||
border: 1px solid $curious-blue;
|
||||
color: $curious-blue;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
|
||||
// Component Colors
|
||||
$tx-view-bg: $white;
|
||||
$wallet-view-bg: $wild-sand;
|
||||
$wallet-view-bg: $alabaster;
|
||||
|
||||
// Main container
|
||||
.main-container {
|
||||
@ -40,6 +40,8 @@ $wallet-view-bg: $wild-sand;
|
||||
|
||||
.open-in-browser {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// wallet view and sidebar
|
||||
@ -47,7 +49,7 @@ $wallet-view-bg: $wild-sand;
|
||||
.wallet-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 33.5 1 33.5%;
|
||||
flex: 32 1 32%;
|
||||
width: 0;
|
||||
background: $wallet-view-bg;
|
||||
z-index: 200;
|
||||
@ -69,22 +71,18 @@ $wallet-view-bg: $wild-sand;
|
||||
}
|
||||
|
||||
&__keyring-label {
|
||||
height: 40px;
|
||||
height: 50px;
|
||||
color: $dusty-gray;
|
||||
font-family: Roboto;
|
||||
font-size: 10px;
|
||||
line-height: 40px;
|
||||
text-align: right;
|
||||
padding: 0 20px;
|
||||
padding: 17px 20px 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&__details-button {
|
||||
color: $curious-blue;
|
||||
font-size: 10px;
|
||||
line-height: 13px;
|
||||
text-align: center;
|
||||
border: 1px solid $curious-blue;
|
||||
border-radius: 10.5px;
|
||||
border-radius: 17px;
|
||||
background-color: transparent;
|
||||
margin: 0 auto;
|
||||
padding: 4px 12px;
|
||||
@ -121,16 +119,14 @@ $wallet-view-bg: $wild-sand;
|
||||
|
||||
&__add-token-button {
|
||||
flex: 0 0 auto;
|
||||
color: $dusty-gray;
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
text-align: center;
|
||||
margin: 36px auto;
|
||||
border: 1px solid $dusty-gray;
|
||||
border-radius: 2px;
|
||||
font-weight: 300;
|
||||
background: none;
|
||||
padding: 9px 30px;
|
||||
padding: .7rem 2rem;
|
||||
transition: border-color .3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +155,7 @@ $wallet-view-bg: $wild-sand;
|
||||
background: rgb(250, 250, 250);
|
||||
z-index: $sidebar-z-index;
|
||||
position: fixed;
|
||||
top: 56px;
|
||||
top: 66px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@ -199,7 +195,7 @@ $wallet-view-bg: $wild-sand;
|
||||
|
||||
.main-container {
|
||||
// margin-top: 6.9vh;
|
||||
width: 85%;
|
||||
width: 85vw;
|
||||
height: 90vh;
|
||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
|
||||
}
|
||||
@ -208,7 +204,7 @@ $wallet-view-bg: $wild-sand;
|
||||
@media screen and (min-width: 769px) {
|
||||
.main-container {
|
||||
// margin-top: 6.9vh;
|
||||
width: 80%;
|
||||
width: 80vw;
|
||||
height: 82vh;
|
||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
|
||||
}
|
||||
@ -217,7 +213,7 @@ $wallet-view-bg: $wild-sand;
|
||||
@media screen and (min-width: 1281px) {
|
||||
.main-container {
|
||||
// margin-top: 6.9vh;
|
||||
width: 65%;
|
||||
width: 62vw;
|
||||
height: 82vh;
|
||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
|
||||
}
|
||||
@ -239,14 +235,6 @@ $wallet-view-bg: $wild-sand;
|
||||
overflow-y: auto;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
button.btn-clear {
|
||||
width: 93px;
|
||||
height: 50px;
|
||||
font-size: .7em;
|
||||
background: $white;
|
||||
border: 1px solid;
|
||||
}
|
||||
}
|
||||
|
||||
// wallet view
|
||||
@ -254,9 +242,9 @@ $wallet-view-bg: $wild-sand;
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
line-height: 20px;
|
||||
color: $scorpion;
|
||||
color: $black;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: .9rem;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
@ -526,8 +526,9 @@
|
||||
}
|
||||
|
||||
&__form {
|
||||
margin: 13px 0;
|
||||
padding: 13px 0;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
padding: 13px 0;
|
||||
@ -651,11 +652,11 @@
|
||||
border: 1px solid $curious-blue;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 14px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
&__sliders-icon {
|
||||
@ -677,38 +678,13 @@
|
||||
border-top: 1px solid $alto;
|
||||
background: $white;
|
||||
padding: 0 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__next-btn,
|
||||
&__cancel-btn,
|
||||
&__next-btn__disabled {
|
||||
width: 163px;
|
||||
text-align: center;
|
||||
height: 55px;
|
||||
border-radius: 2px;
|
||||
background-color: $white;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
line-height: 21px;
|
||||
border: 1px solid;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
&__next-btn,
|
||||
&__next-btn__disabled {
|
||||
color: $curious-blue;
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
|
||||
&__next-btn__disabled {
|
||||
opacity: .5;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
&__cancel-btn {
|
||||
color: $dusty-gray;
|
||||
border-color: $dusty-gray;
|
||||
width: 163px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
&__customize-gas {
|
||||
|
@ -12,7 +12,7 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
||||
position: relative;
|
||||
|
||||
&__token-balance {
|
||||
font-size: 130%;
|
||||
font-size: 1.5rem;
|
||||
|
||||
@media #{$wallet-balance-breakpoint-range} {
|
||||
font-size: 105%;
|
||||
@ -34,7 +34,8 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
||||
}
|
||||
|
||||
&--active {
|
||||
background-color: rgba($wallet-balance-bg, 1);
|
||||
background-color: $manatee;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&__identicon {
|
||||
@ -62,11 +63,11 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
||||
height: 55px;
|
||||
width: 191px;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(0,0,0,0.82);
|
||||
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.5);
|
||||
position: fixed;
|
||||
margin-top: 20px;
|
||||
margin-left: 105px;
|
||||
background-color: rgba(0, 0, 0, .82);
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .5);
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 25px;
|
||||
z-index: 2000;
|
||||
|
||||
&__close-area {
|
||||
|
@ -6,6 +6,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tx-list-header-wrapper {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.tx-list-header {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
@ -32,13 +36,9 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: $break-large) {
|
||||
.tx-list-header-wrapper {
|
||||
flex: 0 0 55px;
|
||||
}
|
||||
|
||||
.tx-list-header {
|
||||
font-size: 16px;
|
||||
margin: 1.5em 2.37em;
|
||||
margin: 1.1em 2.37em .8em;
|
||||
}
|
||||
|
||||
.tx-list-container::-webkit-scrollbar {
|
||||
@ -73,7 +73,7 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: $break-large) {
|
||||
padding-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,21 +91,13 @@
|
||||
}
|
||||
|
||||
.tx-list-date-wrapper {
|
||||
flex: 1 1 auto;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $break-large) {
|
||||
margin-top: 12px;
|
||||
}
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.tx-list-content-wrapper {
|
||||
align-items: stretch;
|
||||
margin-bottom: 4px;
|
||||
margin-top: 2px;
|
||||
flex: 1 0 auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@ -115,7 +107,7 @@
|
||||
font-size: 12px;
|
||||
|
||||
.tx-list-status {
|
||||
font-size: 14px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.tx-list-account {
|
||||
@ -129,7 +121,7 @@
|
||||
|
||||
.tx-list-fiat-value {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -210,7 +202,7 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: $break-large) {
|
||||
margin: 0 2.37em;
|
||||
padding: 0 2.37em;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
@ -259,6 +251,8 @@
|
||||
}
|
||||
|
||||
.tx-list-fiat-value {
|
||||
font-size: 12px;
|
||||
line-height: initial;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
@ -8,7 +8,8 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
||||
background: rgba($wallet-balance-bg, 0);
|
||||
|
||||
&--active {
|
||||
background: rgba($wallet-balance-bg, 1);
|
||||
background: $manatee;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +42,7 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
||||
align-items: flex-start;
|
||||
|
||||
.token-amount {
|
||||
font-size: 135%;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.fiat-amount {
|
||||
@ -61,11 +62,13 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.balance-icon {
|
||||
border-radius: 25px;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border: 1px solid $alto;
|
||||
}
|
||||
}
|
||||
|
||||
.balance-icon {
|
||||
border-radius: 25px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 1px solid $alto;
|
||||
padding: 5px;
|
||||
background: $white;
|
||||
}
|
||||
|
@ -51,14 +51,14 @@
|
||||
|
||||
@font-face {
|
||||
font-family: 'DIN NEXT';
|
||||
src: url('/fonts/DIN NEXT/DIN NEXT W01 Regular.otf') format('opentype');
|
||||
src: url('/fonts/DIN Next/DIN Next W01 Regular.otf') format('opentype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DIN NEXT Light';
|
||||
src: url('/fonts/DIN NEXT/DIN NEXT W10 Light.otf') format('opentype');
|
||||
src: url('/fonts/DIN Next/DIN Next W10 Light.otf') format('opentype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user