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
|
## 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
|
## 3.12.1 2017-11-29
|
||||||
|
|
||||||
- Fix bug where a user could be shown two different seed phrases.
|
- 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)
|
[![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
|
## 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
|
## 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",
|
"name": "MetaMask",
|
||||||
"short_name": "Metamask",
|
"short_name": "Metamask",
|
||||||
"version": "4.0.4",
|
"version": "4.0.9",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"author": "https://metamask.io",
|
"author": "https://metamask.io",
|
||||||
"description": "Ethereum Browser Extension",
|
"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 RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask'
|
||||||
const LOCALHOST_RPC_URL = 'http://localhost:8545'
|
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'
|
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -14,9 +23,22 @@ module.exports = {
|
|||||||
kovan: KOVAN_RPC_URL,
|
kovan: KOVAN_RPC_URL,
|
||||||
rinkeby: RINKEBY_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: {
|
networkNames: {
|
||||||
3: 'Ropsten',
|
3: 'Ropsten',
|
||||||
4: 'Rinkeby',
|
4: 'Rinkeby',
|
||||||
42: 'Kovan',
|
42: 'Kovan',
|
||||||
},
|
},
|
||||||
|
enums: {
|
||||||
|
DEFAULT_RPC,
|
||||||
|
OLD_UI_NETWORK_TYPE,
|
||||||
|
BETA_UI_NETWORK_TYPE,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ function logStreamDisconnectWarning (remoteLabel, err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function shouldInjectWeb3 () {
|
function shouldInjectWeb3 () {
|
||||||
return doctypeCheck() || suffixCheck()
|
return doctypeCheck() && suffixCheck() && documentElementCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
function doctypeCheck () {
|
function doctypeCheck () {
|
||||||
@ -104,7 +104,7 @@ function doctypeCheck () {
|
|||||||
if (doctype) {
|
if (doctype) {
|
||||||
return doctype.name === 'html'
|
return doctype.name === 'html'
|
||||||
} else {
|
} else {
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +121,14 @@ function suffixCheck () {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function documentElementCheck () {
|
||||||
|
var documentElement = document.documentElement.nodeName
|
||||||
|
if (documentElement) {
|
||||||
|
return documentElement.toLowerCase() === 'html'
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
function redirectToPhishingWarning () {
|
function redirectToPhishingWarning () {
|
||||||
console.log('MetaMask - redirecting to phishing warning')
|
console.log('MetaMask - redirecting to phishing warning')
|
||||||
window.location.href = 'https://metamask.io/phishing.html'
|
window.location.href = 'https://metamask.io/phishing.html'
|
||||||
|
@ -57,3 +57,4 @@ class BlacklistController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BlacklistController
|
module.exports = BlacklistController
|
||||||
|
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const EventEmitter = require('events')
|
const EventEmitter = require('events')
|
||||||
const createMetamaskProvider = require('web3-provider-engine/zero.js')
|
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 ObservableStore = require('obs-store')
|
||||||
const ComposedStore = require('obs-store/lib/composed')
|
const ComposedStore = require('obs-store/lib/composed')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const EthQuery = require('eth-query')
|
const EthQuery = require('eth-query')
|
||||||
const createEventEmitterProxy = require('../lib/events-proxy.js')
|
const createEventEmitterProxy = require('../lib/events-proxy.js')
|
||||||
const RPC_ADDRESS_LIST = require('../config.js').network
|
const networkConfig = require('../config.js')
|
||||||
const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby']
|
const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums
|
||||||
|
const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
|
||||||
|
|
||||||
module.exports = class NetworkController extends EventEmitter {
|
module.exports = class NetworkController extends EventEmitter {
|
||||||
|
|
||||||
constructor (config) {
|
constructor (config) {
|
||||||
super()
|
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)
|
config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
|
||||||
this.networkStore = new ObservableStore('loading')
|
this.networkStore = new ObservableStore('loading')
|
||||||
this.providerStore = new ObservableStore(config.provider)
|
this.providerStore = new ObservableStore(config.provider)
|
||||||
@ -22,10 +30,32 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
this.on('networkDidChange', this.lookupNetwork)
|
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) {
|
initializeProvider (_providerParams) {
|
||||||
this._baseProviderParams = _providerParams
|
this._baseProviderParams = _providerParams
|
||||||
const rpcUrl = this.getCurrentRpcAddress()
|
const { type, rpcTarget } = this.providerStore.getState()
|
||||||
this._configureStandardProvider({ rpcUrl })
|
// map rpcTarget to rpcUrl
|
||||||
|
const opts = {
|
||||||
|
type,
|
||||||
|
rpcUrl: rpcTarget,
|
||||||
|
}
|
||||||
|
this._configureProvider(opts)
|
||||||
this._proxy.on('block', this._logBlock.bind(this))
|
this._proxy.on('block', this._logBlock.bind(this))
|
||||||
this._proxy.on('error', this.verifyNetwork.bind(this))
|
this._proxy.on('error', this.verifyNetwork.bind(this))
|
||||||
this.ethQuery = new EthQuery(this._proxy)
|
this.ethQuery = new EthQuery(this._proxy)
|
||||||
@ -76,14 +106,17 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
return this.getRpcAddressForType(provider.type)
|
return this.getRpcAddressForType(provider.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setProviderType (type) {
|
async setProviderType (type, forceUpdate = false) {
|
||||||
assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`)
|
assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`)
|
||||||
// skip if type already matches
|
// skip if type already matches
|
||||||
if (type === this.getProviderConfig().type) return
|
if (type === this.getProviderConfig().type && !forceUpdate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const rpcTarget = this.getRpcAddressForType(type)
|
const rpcTarget = this.getRpcAddressForType(type)
|
||||||
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
|
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
|
||||||
this.providerStore.updateState({ type, rpcTarget })
|
this.providerStore.updateState({ type, rpcTarget })
|
||||||
this._switchNetwork({ rpcUrl: rpcTarget })
|
this._switchNetwork({ type })
|
||||||
}
|
}
|
||||||
|
|
||||||
getProviderConfig () {
|
getProviderConfig () {
|
||||||
@ -91,22 +124,67 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getRpcAddressForType (type, provider = this.getProviderConfig()) {
|
getRpcAddressForType (type, provider = this.getProviderConfig()) {
|
||||||
if (RPC_ADDRESS_LIST[type]) return RPC_ADDRESS_LIST[type]
|
if (this._networkEndpoints[type]) {
|
||||||
return provider && provider.rpcTarget ? provider.rpcTarget : DEFAULT_RPC
|
return this._networkEndpoints[type]
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider && provider.rpcTarget ? provider.rpcTarget : this._defaultRpc
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Private
|
// Private
|
||||||
//
|
//
|
||||||
|
|
||||||
_switchNetwork (providerParams) {
|
_switchNetwork (opts) {
|
||||||
this.setNetworkState('loading')
|
this.setNetworkState('loading')
|
||||||
this._configureStandardProvider(providerParams)
|
this._configureProvider(opts)
|
||||||
this.emit('networkDidChange')
|
this.emit('networkDidChange')
|
||||||
}
|
}
|
||||||
|
|
||||||
_configureStandardProvider (_providerParams) {
|
_configureProvider (opts) {
|
||||||
const providerParams = extend(this._baseProviderParams, _providerParams)
|
// 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)
|
const provider = createMetamaskProvider(providerParams)
|
||||||
this._setProvider(provider)
|
this._setProvider(provider)
|
||||||
}
|
}
|
||||||
|
@ -36,22 +36,24 @@ class PreferencesController {
|
|||||||
return this.store.getState().selectedAddress
|
return this.store.getState().selectedAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
addToken (rawAddress, symbol, decimals) {
|
async addToken (rawAddress, symbol, decimals) {
|
||||||
const address = normalizeAddress(rawAddress)
|
const address = normalizeAddress(rawAddress)
|
||||||
const newEntry = { address, symbol, decimals }
|
const newEntry = { address, symbol, decimals }
|
||||||
|
|
||||||
const tokens = this.store.getState().tokens
|
const tokens = this.store.getState().tokens
|
||||||
const previousIndex = tokens.find((token, index) => {
|
const previousEntry = tokens.find((token, index) => {
|
||||||
return token.address === address
|
return token.address === address
|
||||||
})
|
})
|
||||||
|
const previousIndex = tokens.indexOf(previousEntry)
|
||||||
|
|
||||||
if (previousIndex) {
|
if (previousEntry) {
|
||||||
tokens[previousIndex] = newEntry
|
tokens[previousIndex] = newEntry
|
||||||
} else {
|
} else {
|
||||||
tokens.push(newEntry)
|
tokens.push(newEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store.updateState({ tokens })
|
this.store.updateState({ tokens })
|
||||||
|
|
||||||
return Promise.resolve(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.provider = opts.provider
|
||||||
this.blockTracker = opts.blockTracker
|
this.blockTracker = opts.blockTracker
|
||||||
this.signEthTx = opts.signTransaction
|
this.signEthTx = opts.signTransaction
|
||||||
|
this.getGasPrice = opts.getGasPrice
|
||||||
|
|
||||||
this.memStore = new ObservableStore({})
|
this.memStore = new ObservableStore({})
|
||||||
this.query = new EthQuery(this.provider)
|
this.query = new EthQuery(this.provider)
|
||||||
@ -59,7 +60,6 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
this.pendingTxTracker = new PendingTransactionTracker({
|
this.pendingTxTracker = new PendingTransactionTracker({
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
nonceTracker: this.nonceTracker,
|
nonceTracker: this.nonceTracker,
|
||||||
retryTimePeriod: 86400000, // Retry 3500 blocks, or about 1 day.
|
|
||||||
publishTransaction: (rawTx) => this.query.sendRawTransaction(rawTx),
|
publishTransaction: (rawTx) => this.query.sendRawTransaction(rawTx),
|
||||||
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
|
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
|
||||||
getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
|
getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
|
||||||
@ -138,18 +138,19 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
|
|
||||||
async newUnapprovedTransaction (txParams) {
|
async newUnapprovedTransaction (txParams) {
|
||||||
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
|
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
|
||||||
const txMeta = await this.addUnapprovedTransaction(txParams)
|
const initialTxMeta = await this.addUnapprovedTransaction(txParams)
|
||||||
this.emit('newUnapprovedTx', txMeta)
|
|
||||||
// listen for tx completion (success, fail)
|
// listen for tx completion (success, fail)
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.txStateManager.once(`${txMeta.id}:finished`, (completedTx) => {
|
this.txStateManager.once(`${initialTxMeta.id}:finished`, (finishedTxMeta) => {
|
||||||
switch (completedTx.status) {
|
switch (finishedTxMeta.status) {
|
||||||
case 'submitted':
|
case 'submitted':
|
||||||
return resolve(completedTx.hash)
|
return resolve(finishedTxMeta.hash)
|
||||||
case 'rejected':
|
case 'rejected':
|
||||||
return reject(new Error('MetaMask Tx Signature: User denied transaction signature.'))
|
return reject(new Error('MetaMask Tx Signature: User denied transaction signature.'))
|
||||||
|
case 'failed':
|
||||||
|
return reject(new Error(finishedTxMeta.err.message))
|
||||||
default:
|
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',
|
status: 'unapproved',
|
||||||
metamaskNetworkId: this.getNetwork(),
|
metamaskNetworkId: this.getNetwork(),
|
||||||
txParams: txParams,
|
txParams: txParams,
|
||||||
|
loadingDefaults: true,
|
||||||
}
|
}
|
||||||
|
this.addTx(txMeta)
|
||||||
|
this.emit('newUnapprovedTx', txMeta)
|
||||||
// add default tx params
|
// add default tx params
|
||||||
await this.addTxDefaults(txMeta)
|
await this.addTxDefaults(txMeta)
|
||||||
|
|
||||||
|
txMeta.loadingDefaults = false
|
||||||
// save txMeta
|
// save txMeta
|
||||||
this.addTx(txMeta)
|
this.txStateManager.updateTx(txMeta)
|
||||||
return txMeta
|
return txMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,13 +183,28 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
const txParams = txMeta.txParams
|
const txParams = txMeta.txParams
|
||||||
// ensure value
|
// ensure value
|
||||||
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
|
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.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
|
||||||
txParams.value = txParams.value || '0x0'
|
txParams.value = txParams.value || '0x0'
|
||||||
// set gasLimit
|
// set gasLimit
|
||||||
return await this.txGasUtil.analyzeGasUsage(txMeta)
|
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) {
|
async updateAndApproveTransaction (txMeta) {
|
||||||
this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
|
this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
|
||||||
await this.approveTransaction(txMeta.id)
|
await this.approveTransaction(txMeta.id)
|
||||||
@ -200,7 +221,12 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
// wait for a nonce
|
// wait for a nonce
|
||||||
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
|
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
|
||||||
// add nonce to txParams
|
// 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
|
// add nonce debugging information to txMeta
|
||||||
txMeta.nonceDetails = nonceLock.nonceDetails
|
txMeta.nonceDetails = nonceLock.nonceDetails
|
||||||
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
|
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
|
||||||
|
@ -117,8 +117,6 @@ class AccountTracker extends EventEmitter {
|
|||||||
const query = this._query
|
const query = this._query
|
||||||
async.parallel({
|
async.parallel({
|
||||||
balance: query.getBalance.bind(query, address),
|
balance: query.getBalance.bind(query, address),
|
||||||
nonce: query.getTransactionCount.bind(query, address),
|
|
||||||
code: query.getCode.bind(query, address),
|
|
||||||
}, cb)
|
}, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
|
|||||||
this.query = new EthQuery(config.provider)
|
this.query = new EthQuery(config.provider)
|
||||||
this.nonceTracker = config.nonceTracker
|
this.nonceTracker = config.nonceTracker
|
||||||
// default is one day
|
// default is one day
|
||||||
this.retryTimePeriod = config.retryTimePeriod || 86400000
|
|
||||||
this.getPendingTransactions = config.getPendingTransactions
|
this.getPendingTransactions = config.getPendingTransactions
|
||||||
this.getCompletedTransactions = config.getCompletedTransactions
|
this.getCompletedTransactions = config.getCompletedTransactions
|
||||||
this.publishTransaction = config.publishTransaction
|
this.publishTransaction = config.publishTransaction
|
||||||
@ -106,12 +105,6 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
|
|||||||
this.emit('tx:block-update', txMeta, latestBlockNumber)
|
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 firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber
|
||||||
const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16)
|
const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16)
|
||||||
|
|
||||||
@ -185,7 +178,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _checkIfNonceIsTaken (txMeta) {
|
async _checkIfNonceIsTaken (txMeta) {
|
||||||
const completed = this.getCompletedTransactions()
|
const address = txMeta.txParams.from
|
||||||
|
const completed = this.getCompletedTransactions(address)
|
||||||
const sameNonce = completed.filter((otherMeta) => {
|
const sameNonce = completed.filter((otherMeta) => {
|
||||||
return otherMeta.txParams.nonce === txMeta.txParams.nonce
|
return otherMeta.txParams.nonce === txMeta.txParams.nonce
|
||||||
})
|
})
|
||||||
|
@ -4,6 +4,7 @@ const {
|
|||||||
BnMultiplyByFraction,
|
BnMultiplyByFraction,
|
||||||
bnToHex,
|
bnToHex,
|
||||||
} = require('./util')
|
} = require('./util')
|
||||||
|
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
tx-utils are utility methods for Transaction manager
|
tx-utils are utility methods for Transaction manager
|
||||||
@ -22,7 +23,11 @@ module.exports = class txProvideUtil {
|
|||||||
try {
|
try {
|
||||||
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
||||||
} catch (err) {
|
} 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
|
txMeta.simulationFails = true
|
||||||
return txMeta
|
return txMeta
|
||||||
}
|
}
|
||||||
@ -33,14 +38,30 @@ module.exports = class txProvideUtil {
|
|||||||
|
|
||||||
async estimateTxGas (txMeta, blockGasLimitHex) {
|
async estimateTxGas (txMeta, blockGasLimitHex) {
|
||||||
const txParams = txMeta.txParams
|
const txParams = txMeta.txParams
|
||||||
|
|
||||||
// check if gasLimit is already specified
|
// check if gasLimit is already specified
|
||||||
txMeta.gasLimitSpecified = Boolean(txParams.gas)
|
txMeta.gasLimitSpecified = Boolean(txParams.gas)
|
||||||
// if not, fallback to block gasLimit
|
|
||||||
if (!txMeta.gasLimitSpecified) {
|
// if it is, use that value
|
||||||
const blockGasLimitBN = hexToBn(blockGasLimitHex)
|
if (txMeta.gasLimitSpecified) {
|
||||||
const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20)
|
return txParams.gas
|
||||||
txParams.gas = bnToHex(saferGasLimitBN)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// run tx
|
||||||
return await this.query.estimateGas(txParams)
|
return await this.query.estimateGas(txParams)
|
||||||
}
|
}
|
||||||
@ -51,7 +72,7 @@ module.exports = class txProvideUtil {
|
|||||||
|
|
||||||
// if gasLimit was specified and doesnt OOG,
|
// if gasLimit was specified and doesnt OOG,
|
||||||
// use original specified amount
|
// use original specified amount
|
||||||
if (txMeta.gasLimitSpecified) {
|
if (txMeta.gasLimitSpecified || txMeta.simpleSend) {
|
||||||
txMeta.estimatedGas = txParams.gas
|
txMeta.estimatedGas = txParams.gas
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -77,8 +98,26 @@ module.exports = class txProvideUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async validateTxParams (txParams) {
|
async validateTxParams (txParams) {
|
||||||
if (('value' in txParams) && txParams.value.indexOf('-') === 0) {
|
this.validateRecipient(txParams)
|
||||||
throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
|
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')
|
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'.
|
// should update the status of the tx to 'approved'.
|
||||||
setTxStatusApproved (txId) {
|
setTxStatusApproved (txId) {
|
||||||
this._setTxStatus(txId, 'approved')
|
this._setTxStatus(txId, 'approved')
|
||||||
@ -236,7 +240,7 @@ module.exports = class TransactionStateManger extends EventEmitter {
|
|||||||
txMeta.status = status
|
txMeta.status = status
|
||||||
this.emit(`${txMeta.id}:${status}`, txId)
|
this.emit(`${txMeta.id}:${status}`, txId)
|
||||||
this.emit(`tx:status-update`, txId, status)
|
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.emit(`${txMeta.id}:finished`, txMeta)
|
||||||
}
|
}
|
||||||
this.updateTx(txMeta, `txStateManager: setting status to ${status}`)
|
this.updateTx(txMeta, `txStateManager: setting status to ${status}`)
|
||||||
|
@ -5,7 +5,6 @@ const Dnode = require('dnode')
|
|||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
const asStream = require('obs-store/lib/asStream')
|
const asStream = require('obs-store/lib/asStream')
|
||||||
const AccountTracker = require('./lib/account-tracker')
|
const AccountTracker = require('./lib/account-tracker')
|
||||||
const EthQuery = require('eth-query')
|
|
||||||
const RpcEngine = require('json-rpc-engine')
|
const RpcEngine = require('json-rpc-engine')
|
||||||
const debounce = require('debounce')
|
const debounce = require('debounce')
|
||||||
const createEngineStream = require('json-rpc-middleware-stream/engineStream')
|
const createEngineStream = require('json-rpc-middleware-stream/engineStream')
|
||||||
@ -23,6 +22,7 @@ const ShapeShiftController = require('./controllers/shapeshift')
|
|||||||
const AddressBookController = require('./controllers/address-book')
|
const AddressBookController = require('./controllers/address-book')
|
||||||
const InfuraController = require('./controllers/infura')
|
const InfuraController = require('./controllers/infura')
|
||||||
const BlacklistController = require('./controllers/blacklist')
|
const BlacklistController = require('./controllers/blacklist')
|
||||||
|
const RecentBlocksController = require('./controllers/recent-blocks')
|
||||||
const MessageManager = require('./lib/message-manager')
|
const MessageManager = require('./lib/message-manager')
|
||||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||||
const TypedMessageManager = require('./lib/typed-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 getBuyEthUrl = require('./lib/buy-eth-url')
|
||||||
const Mutex = require('await-semaphore').Mutex
|
const Mutex = require('await-semaphore').Mutex
|
||||||
const version = require('../manifest.json').version
|
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 {
|
module.exports = class MetamaskController extends EventEmitter {
|
||||||
|
|
||||||
constructor (opts) {
|
constructor (opts) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
|
||||||
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
|
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
|
||||||
|
|
||||||
this.opts = opts
|
this.opts = opts
|
||||||
@ -91,8 +93,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.provider = this.initializeProvider()
|
this.provider = this.initializeProvider()
|
||||||
this.blockTracker = this.provider._blockTracker
|
this.blockTracker = this.provider._blockTracker
|
||||||
|
|
||||||
// eth data query tools
|
this.recentBlocksController = new RecentBlocksController({
|
||||||
this.ethQuery = new EthQuery(this.provider)
|
blockTracker: this.blockTracker,
|
||||||
|
provider: this.provider,
|
||||||
|
})
|
||||||
|
|
||||||
// account tracker watches balances, nonces, and any code at their address.
|
// account tracker watches balances, nonces, and any code at their address.
|
||||||
this.accountTracker = new AccountTracker({
|
this.accountTracker = new AccountTracker({
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
@ -133,7 +138,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
|
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
blockTracker: this.blockTracker,
|
blockTracker: this.blockTracker,
|
||||||
ethQuery: this.ethQuery,
|
getGasPrice: this.getGasPrice.bind(this),
|
||||||
})
|
})
|
||||||
this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts))
|
this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts))
|
||||||
|
|
||||||
@ -196,25 +201,30 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.blacklistController.store.subscribe((state) => {
|
this.blacklistController.store.subscribe((state) => {
|
||||||
this.store.updateState({ BlacklistController: state })
|
this.store.updateState({ BlacklistController: state })
|
||||||
})
|
})
|
||||||
|
this.recentBlocksController.store.subscribe((state) => {
|
||||||
|
this.store.updateState({ RecentBlocks: state })
|
||||||
|
})
|
||||||
this.infuraController.store.subscribe((state) => {
|
this.infuraController.store.subscribe((state) => {
|
||||||
this.store.updateState({ InfuraController: state })
|
this.store.updateState({ InfuraController: state })
|
||||||
})
|
})
|
||||||
|
|
||||||
// manual mem state subscriptions
|
// manual mem state subscriptions
|
||||||
this.networkController.store.subscribe(this.sendUpdate.bind(this))
|
const sendUpdate = this.sendUpdate.bind(this)
|
||||||
this.accountTracker.store.subscribe(this.sendUpdate.bind(this))
|
this.networkController.store.subscribe(sendUpdate)
|
||||||
this.txController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.accountTracker.store.subscribe(sendUpdate)
|
||||||
this.balancesController.store.subscribe(this.sendUpdate.bind(this))
|
this.txController.memStore.subscribe(sendUpdate)
|
||||||
this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
this.balancesController.store.subscribe(sendUpdate)
|
||||||
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
this.messageManager.memStore.subscribe(sendUpdate)
|
||||||
this.typedMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
this.personalMessageManager.memStore.subscribe(sendUpdate)
|
||||||
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.typedMessageManager.memStore.subscribe(sendUpdate)
|
||||||
this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
|
this.keyringController.memStore.subscribe(sendUpdate)
|
||||||
this.addressBookController.store.subscribe(this.sendUpdate.bind(this))
|
this.preferencesController.store.subscribe(sendUpdate)
|
||||||
this.currencyController.store.subscribe(this.sendUpdate.bind(this))
|
this.recentBlocksController.store.subscribe(sendUpdate)
|
||||||
this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.addressBookController.store.subscribe(sendUpdate)
|
||||||
this.shapeshiftController.store.subscribe(this.sendUpdate.bind(this))
|
this.currencyController.store.subscribe(sendUpdate)
|
||||||
this.infuraController.store.subscribe(this.sendUpdate.bind(this))
|
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.currencyController.store.getState(),
|
||||||
this.noticeController.memStore.getState(),
|
this.noticeController.memStore.getState(),
|
||||||
this.infuraController.store.getState(),
|
this.infuraController.store.getState(),
|
||||||
|
this.recentBlocksController.store.getState(),
|
||||||
// config manager
|
// config manager
|
||||||
this.configManager.getConfig(),
|
this.configManager.getConfig(),
|
||||||
this.shapeshiftController.store.getState(),
|
this.shapeshiftController.store.getState(),
|
||||||
@ -342,6 +353,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
submitPassword: nodeify(keyringController.submitPassword, keyringController),
|
submitPassword: nodeify(keyringController.submitPassword, keyringController),
|
||||||
|
|
||||||
// network management
|
// network management
|
||||||
|
setNetworkEndpoints: nodeify(networkController.setNetworkEndpoints, networkController),
|
||||||
setProviderType: nodeify(networkController.setProviderType, networkController),
|
setProviderType: nodeify(networkController.setProviderType, networkController),
|
||||||
setCustomRpc: nodeify(this.setCustomRpc, this),
|
setCustomRpc: nodeify(this.setCustomRpc, this),
|
||||||
|
|
||||||
@ -365,7 +377,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
// txController
|
// txController
|
||||||
cancelTransaction: nodeify(txController.cancelTransaction, txController),
|
cancelTransaction: nodeify(txController.cancelTransaction, txController),
|
||||||
|
updateTransaction: nodeify(txController.updateTransaction, txController),
|
||||||
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
|
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
|
||||||
|
retryTransaction: nodeify(this.retryTransaction, this),
|
||||||
|
|
||||||
// messageManager
|
// messageManager
|
||||||
signMessage: nodeify(this.signMessage, this),
|
signMessage: nodeify(this.signMessage, this),
|
||||||
@ -475,6 +489,33 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.emit('update', this.getState())
|
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
|
// Vault Management
|
||||||
//
|
//
|
||||||
@ -504,10 +545,15 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
async createNewVaultAndRestore (password, seed) {
|
async createNewVaultAndRestore (password, seed) {
|
||||||
const release = await this.createVaultMutex.acquire()
|
const release = await this.createVaultMutex.acquire()
|
||||||
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
|
try {
|
||||||
this.selectFirstIdentity(vault)
|
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
|
||||||
release()
|
this.selectFirstIdentity(vault)
|
||||||
return vault
|
release()
|
||||||
|
return vault
|
||||||
|
} catch (err) {
|
||||||
|
release()
|
||||||
|
throw err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectFirstIdentity (vault) {
|
selectFirstIdentity (vault) {
|
||||||
@ -576,6 +622,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
//
|
//
|
||||||
// Identity Management
|
// Identity Management
|
||||||
//
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
async retryTransaction (txId, cb) {
|
||||||
|
await this.txController.retryTransaction(txId)
|
||||||
|
const state = await this.getState()
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
newUnsignedMessage (msgParams, cb) {
|
newUnsignedMessage (msgParams, cb) {
|
||||||
const msgId = this.messageManager.addUnapprovedMessage(msgParams)
|
const msgId = this.messageManager.addUnapprovedMessage(msgParams)
|
||||||
|
@ -77,7 +77,7 @@ module.exports = class NoticeController extends EventEmitter {
|
|||||||
return uniqBy(oldNotices.concat(newNotices), 'id')
|
return uniqBy(oldNotices.concat(newNotices), 'id')
|
||||||
}
|
}
|
||||||
|
|
||||||
_filterNotices(notices) {
|
_filterNotices (notices) {
|
||||||
return notices.filter((newNotice) => {
|
return notices.filter((newNotice) => {
|
||||||
if ('version' in newNotice) {
|
if ('version' in newNotice) {
|
||||||
const satisfied = semver.satisfies(this.version, newNotice.version)
|
const satisfied = semver.satisfies(this.version, newNotice.version)
|
||||||
|
@ -26,8 +26,17 @@ const container = document.getElementById('app-content')
|
|||||||
startPopup({ container, connectionStream }, (err, store) => {
|
startPopup({ container, connectionStream }, (err, store) => {
|
||||||
if (err) return displayCriticalError(err)
|
if (err) return displayCriticalError(err)
|
||||||
|
|
||||||
let betaUIState = store.getState().metamask.featureFlags.betaUI
|
// Code commented out until we begin auto adding users to NewUI
|
||||||
let css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
|
// 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 deleteInjectedCss = injectCss(css)
|
||||||
let newBetaUIState
|
let newBetaUIState
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"frequentRpcList": [],
|
"frequentRpcList": [],
|
||||||
"unapprovedTxs": {},
|
"unapprovedTxs": {},
|
||||||
"currentCurrency": "USD",
|
"currentCurrency": "USD",
|
||||||
|
"featureFlags": {"betaUI": true},
|
||||||
"conversionRate": 12.7527416,
|
"conversionRate": 12.7527416,
|
||||||
"conversionDate": 1487624341,
|
"conversionDate": 1487624341,
|
||||||
"noActiveNotices": false,
|
"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:
|
ports:
|
||||||
- "9001"
|
- "9001"
|
||||||
environment:
|
environment:
|
||||||
MASCARA_ORIGIN: "https://zero.metamask.io"
|
MASCARA_ORIGIN: "https://wallet.metamask.io"
|
||||||
VIRTUAL_PORT: "9001"
|
VIRTUAL_PORT: "9001"
|
||||||
VIRTUAL_HOST: "zero.metamask.io"
|
VIRTUAL_HOST: "wallet.metamask.io"
|
||||||
LETSENCRYPT_HOST: "zero.metamask.io"
|
LETSENCRYPT_HOST: "wallet.metamask.io"
|
||||||
LETSENCRYPT_EMAIL: "admin@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 gulpif = require('gulp-if')
|
||||||
var replace = require('gulp-replace')
|
var replace = require('gulp-replace')
|
||||||
var mkdirp = require('mkdirp')
|
var mkdirp = require('mkdirp')
|
||||||
|
var asyncEach = require('async/each')
|
||||||
|
var exec = require('child_process').exec
|
||||||
var sass = require('gulp-sass')
|
var sass = require('gulp-sass')
|
||||||
var autoprefixer = require('gulp-autoprefixer')
|
var autoprefixer = require('gulp-autoprefixer')
|
||||||
var gulpStylelint = require('gulp-stylelint')
|
var gulpStylelint = require('gulp-stylelint')
|
||||||
var stylefmt = require('gulp-stylefmt')
|
var stylefmt = require('gulp-stylefmt')
|
||||||
|
var uglify = require('gulp-uglify-es').default
|
||||||
|
var babel = require('gulp-babel')
|
||||||
|
|
||||||
|
|
||||||
var disableDebugTools = gutil.env.disableDebugTools
|
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'))
|
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
|
// lint js
|
||||||
|
|
||||||
gulp.task('lint', function () {
|
gulp.task('lint', function () {
|
||||||
@ -232,8 +248,18 @@ var jsDevStrings = jsFiles.map(jsFile => `dev:js:${jsFile}`)
|
|||||||
var jsBuildStrings = jsFiles.map(jsFile => `build:js:${jsFile}`)
|
var jsBuildStrings = jsFiles.map(jsFile => `build:js:${jsFile}`)
|
||||||
|
|
||||||
jsFiles.forEach((jsFile) => {
|
jsFiles.forEach((jsFile) => {
|
||||||
gulp.task(`dev:js:${jsFile}`, bundleTask({ watch: true, label: jsFile, filename: `${jsFile}.js` }))
|
gulp.task(`dev:js:${jsFile}`, bundleTask({
|
||||||
gulp.task(`build:js:${jsFile}`, bundleTask({ watch: false, label: jsFile, filename: `${jsFile}.js` }))
|
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:
|
// 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:opera', zipTask('opera'))
|
||||||
gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip: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
|
// high level tasks
|
||||||
|
|
||||||
gulp.task('dev', gulp.series('build:scss', 'dev:js', 'copy', gulp.parallel('watch:scss', 'copy:watch', 'dev:reload')))
|
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('build', gulp.series('clean', 'build:scss', gulp.parallel('build:js', 'copy', 'deps')))
|
||||||
gulp.task('dist', gulp.series('build', 'zip'))
|
gulp.task('dist', gulp.series('apply-prod-environment', 'build', 'zip'))
|
||||||
|
|
||||||
// task generators
|
// task generators
|
||||||
|
|
||||||
@ -365,7 +397,6 @@ function bundleTask(opts) {
|
|||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// convert bundle stream to gulp vinyl stream
|
// convert bundle stream to gulp vinyl stream
|
||||||
.pipe(source(opts.filename))
|
.pipe(source(opts.filename))
|
||||||
// inject variables into bundle
|
// inject variables into bundle
|
||||||
@ -375,6 +406,8 @@ function bundleTask(opts) {
|
|||||||
// sourcemaps
|
// sourcemaps
|
||||||
// loads map from browserify file
|
// loads map from browserify file
|
||||||
.pipe(gulpif(debug, sourcemaps.init({ loadMaps: true })))
|
.pipe(gulpif(debug, sourcemaps.init({ loadMaps: true })))
|
||||||
|
// Minification
|
||||||
|
.pipe(gulpif(opts.isBuild, uglify()))
|
||||||
// writes .map file
|
// writes .map file
|
||||||
.pipe(gulpif(debug, sourcemaps.write('./')))
|
.pipe(gulpif(debug, sourcemaps.write('./')))
|
||||||
// write completed bundles
|
// write completed bundles
|
||||||
|
@ -2,6 +2,7 @@ const path = require('path')
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const createBundle = require('./util').createBundle
|
const createBundle = require('./util').createBundle
|
||||||
const serveBundle = require('./util').serveBundle
|
const serveBundle = require('./util').serveBundle
|
||||||
|
const compression = require('compression')
|
||||||
|
|
||||||
module.exports = createMetamascaraServer
|
module.exports = createMetamascaraServer
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ function createMetamascaraServer () {
|
|||||||
|
|
||||||
// serve bundles
|
// serve bundles
|
||||||
const server = express()
|
const server = express()
|
||||||
|
server.use(compression())
|
||||||
|
|
||||||
// ui window
|
// ui window
|
||||||
serveBundle(server, '/ui.js', uiBundle)
|
serveBundle(server, '/ui.js', uiBundle)
|
||||||
server.use(express.static(path.join(__dirname, '/../ui/'), { setHeaders: (res) => res.set('X-Frame-Options', 'DENY') }))
|
server.use(express.static(path.join(__dirname, '/../ui/'), { setHeaders: (res) => res.set('X-Frame-Options', 'DENY') }))
|
||||||
|
@ -23,7 +23,9 @@ function createBundle (entryPoint) {
|
|||||||
cache: {},
|
cache: {},
|
||||||
packageCache: {},
|
packageCache: {},
|
||||||
plugin: [watchify],
|
plugin: [watchify],
|
||||||
}).transform('babelify')
|
})
|
||||||
|
.transform('babelify')
|
||||||
|
.transform('uglifyify', { global: true })
|
||||||
|
|
||||||
bundler.on('update', bundle)
|
bundler.on('update', bundle)
|
||||||
bundle()
|
bundle()
|
||||||
|
@ -5,6 +5,8 @@ import { createNewVaultAndKeychain } from '../../../../ui/app/actions'
|
|||||||
import LoadingScreen from './loading-screen'
|
import LoadingScreen from './loading-screen'
|
||||||
import Breadcrumbs from './breadcrumbs'
|
import Breadcrumbs from './breadcrumbs'
|
||||||
import { DEFAULT_ROUTE, IMPORT_ACCOUNT_ROUTE } from '../../../../ui/app/routes'
|
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 {
|
class CreatePasswordScreen extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -20,6 +22,11 @@ class CreatePasswordScreen extends Component {
|
|||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
this.animationEventEmitter = new EventEmitter()
|
||||||
|
}
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
const { isInitialized, isUnlocked, history } = this.props
|
const { isInitialized, isUnlocked, history } = this.props
|
||||||
if (isInitialized || isUnlocked) {
|
if (isInitialized || isUnlocked) {
|
||||||
@ -56,12 +63,25 @@ class CreatePasswordScreen extends Component {
|
|||||||
render () {
|
render () {
|
||||||
const { isLoading } = this.props
|
const { isLoading } = this.props
|
||||||
|
|
||||||
return (
|
return isLoading
|
||||||
<div className="first-time-flow">
|
? <LoadingScreen loadingMessage="Creating your new account" />
|
||||||
{
|
: (
|
||||||
isLoading
|
<div>
|
||||||
? <LoadingScreen loadingMessage="Creating your new account" />
|
<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">
|
||||||
<div className="create-password__title">
|
<div className="create-password__title">
|
||||||
Create Password
|
Create Password
|
||||||
@ -109,10 +129,9 @@ class CreatePasswordScreen extends Component {
|
|||||||
{ */ }
|
{ */ }
|
||||||
<Breadcrumbs total={3} currentIndex={0} />
|
<Breadcrumbs total={3} currentIndex={0} />
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
}
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
.first-time-flow {
|
.first-time-flow {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
@ -5,6 +6,36 @@
|
|||||||
overflow: auto;
|
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,
|
.create-password,
|
||||||
.unique-image,
|
.unique-image,
|
||||||
.tou,
|
.tou,
|
||||||
@ -540,11 +571,10 @@ button.backup-phrase__confirm-seed-option:hover {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
margin: 35px 0 14px;
|
margin: 35px 0 14px;
|
||||||
transition: 200ms ease-in-out;
|
transition: 200ms ease-in-out;
|
||||||
background: #f7861c;
|
background-color: rgba(247, 134, 28, 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.first-time-flow__button[disabled] {
|
button.first-time-flow__button[disabled] {
|
||||||
background-color: rgba(247, 134, 28, 0.9);
|
|
||||||
opacity: .6;
|
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.
|
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,
|
address: selected,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
h('div.flex-column', {
|
h('flex-column', {
|
||||||
style: {
|
style: {
|
||||||
lineHeight: '10px',
|
lineHeight: '10px',
|
||||||
|
marginLeft: '15px',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
@ -101,7 +102,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'flex-start',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -131,6 +132,8 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
AccountDropdowns,
|
AccountDropdowns,
|
||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
|
marginRight: '8px',
|
||||||
|
marginLeft: 'auto',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
},
|
},
|
||||||
selected,
|
selected,
|
||||||
@ -144,6 +147,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
]),
|
]),
|
||||||
h('.flex-row', {
|
h('.flex-row', {
|
||||||
style: {
|
style: {
|
||||||
|
width: '15em',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'baseline',
|
alignItems: 'baseline',
|
||||||
},
|
},
|
||||||
@ -157,10 +161,10 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
paddingTop: '3px',
|
paddingTop: '3px',
|
||||||
width: '5em',
|
width: '5em',
|
||||||
|
height: '15px',
|
||||||
fontSize: '13px',
|
fontSize: '13px',
|
||||||
fontFamily: 'Montserrat Light',
|
fontFamily: 'Montserrat Light',
|
||||||
textRendering: 'geometricPrecision',
|
textRendering: 'geometricPrecision',
|
||||||
marginTop: '15px',
|
|
||||||
marginBottom: '15px',
|
marginBottom: '15px',
|
||||||
color: '#AEAEAE',
|
color: '#AEAEAE',
|
||||||
},
|
},
|
||||||
@ -188,21 +192,20 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('div', {}, [
|
h('.flex-grow'),
|
||||||
|
|
||||||
h('button', {
|
h('button', {
|
||||||
onClick: () => props.dispatch(actions.buyEthView(selected)),
|
onClick: () => props.dispatch(actions.buyEthView(selected)),
|
||||||
style: { marginRight: '10px' },
|
style: { marginRight: '10px' },
|
||||||
}, 'BUY'),
|
}, 'BUY'),
|
||||||
|
|
||||||
h('button', {
|
h('button', {
|
||||||
onClick: () => props.dispatch(actions.showSendPage()),
|
onClick: () => props.dispatch(actions.showSendPage()),
|
||||||
style: {
|
style: {
|
||||||
marginBottom: '20px',
|
marginBottom: '20px',
|
||||||
},
|
marginRight: '8px',
|
||||||
}, 'SEND'),
|
},
|
||||||
|
}, 'SEND'),
|
||||||
]),
|
|
||||||
|
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
@ -34,6 +34,7 @@ const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
|
|||||||
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
||||||
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
|
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
|
||||||
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
|
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
|
||||||
|
const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(App)
|
module.exports = connect(mapStateToProps)(App)
|
||||||
|
|
||||||
@ -396,7 +397,7 @@ App.prototype.renderDropdown = function () {
|
|||||||
h(DropdownMenuItem, {
|
h(DropdownMenuItem, {
|
||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
onClick: () => { this.props.dispatch(actions.lockMetamask()) },
|
onClick: () => { this.props.dispatch(actions.lockMetamask()) },
|
||||||
}, 'Lock'),
|
}, 'Log Out'),
|
||||||
|
|
||||||
h(DropdownMenuItem, {
|
h(DropdownMenuItem, {
|
||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
@ -405,7 +406,10 @@ App.prototype.renderDropdown = function () {
|
|||||||
|
|
||||||
h(DropdownMenuItem, {
|
h(DropdownMenuItem, {
|
||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
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!'),
|
}, '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
|
// show initialize screen
|
||||||
if (!props.isInitialized || props.forgottenPassword) {
|
if (!props.isInitialized || props.forgottenPassword) {
|
||||||
// show current view
|
// 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
|
// show current view
|
||||||
switch (props.currentView.name) {
|
switch (props.currentView.name) {
|
||||||
|
|
||||||
|
@ -38,6 +38,16 @@ PendingTx.prototype.render = function () {
|
|||||||
const txMeta = this.gatherTxMeta()
|
const txMeta = this.gatherTxMeta()
|
||||||
const txParams = txMeta.txParams || {}
|
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
|
// Account Details
|
||||||
const address = txParams.from || props.selectedAddress
|
const address = txParams.from || props.selectedAddress
|
||||||
const identity = props.identities[address] || { address: address }
|
const identity = props.identities[address] || { address: address }
|
||||||
@ -199,7 +209,7 @@ PendingTx.prototype.render = function () {
|
|||||||
precision: 9,
|
precision: 9,
|
||||||
scale: 9,
|
scale: 9,
|
||||||
suffix: 'GWEI',
|
suffix: 'GWEI',
|
||||||
min: MIN_GAS_PRICE_BN,
|
min: forceGasMin || MIN_GAS_PRICE_BN,
|
||||||
style: {
|
style: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
top: '5px',
|
top: '5px',
|
||||||
|
@ -4,6 +4,7 @@ const h = require('react-hyperscript')
|
|||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('../../ui/app/actions')
|
const actions = require('../../ui/app/actions')
|
||||||
const NetworkIndicator = require('./components/network')
|
const NetworkIndicator = require('./components/network')
|
||||||
|
const LoadingIndicator = require('./components/loading')
|
||||||
const txHelper = require('../lib/tx-helper')
|
const txHelper = require('../lib/tx-helper')
|
||||||
const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
|
const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
|
||||||
|
|
||||||
@ -60,6 +61,11 @@ ConfirmTxScreen.prototype.render = function () {
|
|||||||
|
|
||||||
h('.flex-column.flex-grow', [
|
h('.flex-column.flex-grow', [
|
||||||
|
|
||||||
|
h(LoadingIndicator, {
|
||||||
|
isLoading: txData.loadingDefaults,
|
||||||
|
loadingMessage: 'Estimating transaction cost…',
|
||||||
|
}),
|
||||||
|
|
||||||
// subtitle and nav
|
// subtitle and nav
|
||||||
h('.section-title.flex-row.flex-center', [
|
h('.section-title.flex-row.flex-center', [
|
||||||
!isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
!isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
||||||
|
@ -119,7 +119,7 @@ ConfigScreen.prototype.render = function () {
|
|||||||
if (err) {
|
if (err) {
|
||||||
state.dispatch(actions.displayWarning('Error in retrieving state logs.'))
|
state.dispatch(actions.displayWarning('Error in retrieving state logs.'))
|
||||||
} else {
|
} else {
|
||||||
exportAsFile('MetaMask State Logs', result)
|
exportAsFile('MetaMask State Logs.json', result)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,7 @@ html, body {
|
|||||||
background: #F7F7F7;
|
background: #F7F7F7;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@ -107,6 +108,10 @@ button:not([disabled]):active, input[type="submit"]:not([disabled]):active {
|
|||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grow-on-hover:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@ -436,12 +441,14 @@ input.large-input {
|
|||||||
.account-detail-section {
|
.account-detail-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
max-height: 465px;
|
||||||
flex-direction: inherit;
|
flex-direction: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.name-label {
|
.account-detail-section .name-label {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grow-tenx {
|
.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 recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '')
|
||||||
const nickname = state.nickname || ' '
|
const nickname = state.nickname || ' '
|
||||||
const input = document.querySelector('input[name="amount"]').value
|
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 value = util.normalizeEthStringToWei(input)
|
||||||
const txData = document.querySelector('input[name="txData"]').value
|
const txData = document.querySelector('input[name="txData"]').value
|
||||||
const balance = this.props.balance
|
const balance = this.props.balance
|
||||||
let message
|
|
||||||
|
|
||||||
if (value.gt(balance)) {
|
if (value.gt(balance)) {
|
||||||
message = 'Insufficient funds.'
|
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 ./",
|
"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 ./",
|
"mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
||||||
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
|
"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": "npm run dist:clear && npm install && gulp dist",
|
||||||
"dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
|
"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": "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:single": "METAMASK_ENV=test mocha --require test/helper.js",
|
||||||
"test:integration": "gulp build:scss && npm run test:flat && npm run test:mascara",
|
"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",
|
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
|
||||||
@ -77,14 +77,14 @@
|
|||||||
"eslint-plugin-react": "^7.4.0",
|
"eslint-plugin-react": "^7.4.0",
|
||||||
"eth-bin-to-ops": "^1.0.1",
|
"eth-bin-to-ops": "^1.0.1",
|
||||||
"eth-block-tracker": "^2.2.0",
|
"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-contract-metadata": "^1.1.5",
|
||||||
"eth-hd-keyring": "^1.2.1",
|
"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-phishing-detect": "^1.1.4",
|
||||||
"eth-query": "^2.1.2",
|
"eth-query": "^2.1.2",
|
||||||
"eth-sig-util": "^1.4.0",
|
"eth-sig-util": "^1.4.2",
|
||||||
"eth-simple-keyring": "^1.2.0",
|
|
||||||
"eth-token-tracker": "^1.1.4",
|
"eth-token-tracker": "^1.1.4",
|
||||||
"ethereumjs-abi": "^0.6.4",
|
"ethereumjs-abi": "^0.6.4",
|
||||||
"ethereumjs-tx": "^1.3.0",
|
"ethereumjs-tx": "^1.3.0",
|
||||||
@ -129,6 +129,7 @@
|
|||||||
"obj-multiplex": "^1.0.0",
|
"obj-multiplex": "^1.0.0",
|
||||||
"obs-store": "^3.0.0",
|
"obs-store": "^3.0.0",
|
||||||
"once": "^1.3.3",
|
"once": "^1.3.3",
|
||||||
|
"percentile": "^1.2.0",
|
||||||
"ping-pong-stream": "^1.0.0",
|
"ping-pong-stream": "^1.0.0",
|
||||||
"pojo-migrator": "^2.1.0",
|
"pojo-migrator": "^2.1.0",
|
||||||
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
||||||
@ -162,15 +163,15 @@
|
|||||||
"request-promise": "^4.2.1",
|
"request-promise": "^4.2.1",
|
||||||
"sandwich-expando": "^1.1.3",
|
"sandwich-expando": "^1.1.3",
|
||||||
"semaphore": "^1.0.5",
|
"semaphore": "^1.0.5",
|
||||||
"shallow-copy": "0.0.1",
|
|
||||||
"semver": "^5.4.1",
|
"semver": "^5.4.1",
|
||||||
|
"shallow-copy": "0.0.1",
|
||||||
"sw-stream": "^2.0.0",
|
"sw-stream": "^2.0.0",
|
||||||
"textarea-caret": "^3.0.1",
|
"textarea-caret": "^3.0.1",
|
||||||
"through2": "^2.0.3",
|
"through2": "^2.0.3",
|
||||||
"valid-url": "^1.0.9",
|
"valid-url": "^1.0.9",
|
||||||
"vreme": "^3.0.2",
|
"vreme": "^3.0.2",
|
||||||
"web3": "^0.20.1",
|
"web3": "^0.20.1",
|
||||||
"web3-provider-engine": "^13.3.2",
|
"web3-provider-engine": "^13.5.0",
|
||||||
"web3-stream-provider": "^3.0.1",
|
"web3-stream-provider": "^3.0.1",
|
||||||
"xtend": "^4.0.1"
|
"xtend": "^4.0.1"
|
||||||
},
|
},
|
||||||
@ -200,16 +201,20 @@
|
|||||||
"eth-json-rpc-middleware": "^1.2.7",
|
"eth-json-rpc-middleware": "^1.2.7",
|
||||||
"fs-promise": "^2.0.3",
|
"fs-promise": "^2.0.3",
|
||||||
"gulp": "github:gulpjs/gulp#4.0",
|
"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-json-editor": "^2.2.1",
|
||||||
"gulp-livereload": "^3.8.1",
|
"gulp-livereload": "^3.8.1",
|
||||||
"gulp-replace": "^0.6.1",
|
"gulp-replace": "^0.6.1",
|
||||||
"gulp-sourcemaps": "^2.6.0",
|
"gulp-sourcemaps": "^2.6.0",
|
||||||
"gulp-stylefmt": "^1.1.0",
|
"gulp-stylefmt": "^1.1.0",
|
||||||
"gulp-stylelint": "^4.0.0",
|
"gulp-stylelint": "^4.0.0",
|
||||||
|
"gulp-uglify": "^3.0.0",
|
||||||
|
"gulp-uglify-es": "^1.0.0",
|
||||||
"gulp-util": "^3.0.7",
|
"gulp-util": "^3.0.7",
|
||||||
"gulp-watch": "^4.3.5",
|
"gulp-watch": "^4.3.5",
|
||||||
"gulp-zip": "^4.0.0",
|
"gulp-zip": "^4.0.0",
|
||||||
|
"gulp-eslint": "^4.0.0",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"jsdom": "^11.1.0",
|
"jsdom": "^11.1.0",
|
||||||
"jsdom-global": "^3.0.2",
|
"jsdom-global": "^3.0.2",
|
||||||
@ -239,8 +244,8 @@
|
|||||||
"tape": "^4.5.1",
|
"tape": "^4.5.1",
|
||||||
"testem": "^1.10.3",
|
"testem": "^1.10.3",
|
||||||
"uglifyify": "^4.0.2",
|
"uglifyify": "^4.0.2",
|
||||||
"vinyl-buffer": "^1.0.0",
|
"vinyl-buffer": "^1.0.1",
|
||||||
"vinyl-source-stream": "^1.1.0",
|
"vinyl-source-stream": "^2.0.0",
|
||||||
"watchify": "^3.9.0"
|
"watchify": "^3.9.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -54,6 +54,8 @@ module.exports = function(config) {
|
|||||||
|
|
||||||
// Concurrency level
|
// Concurrency level
|
||||||
// how many browser should be started simultaneous
|
// how many browser should be started simultaneous
|
||||||
concurrency: Infinity
|
concurrency: 1,
|
||||||
|
|
||||||
|
nocache: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@ module.exports = {
|
|||||||
createEngineForTestData,
|
createEngineForTestData,
|
||||||
providerFromEngine,
|
providerFromEngine,
|
||||||
scaffoldMiddleware,
|
scaffoldMiddleware,
|
||||||
createStubedProvider
|
createEthJsQueryStub,
|
||||||
|
createStubedProvider,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -18,6 +19,18 @@ function providerFromEngine (engine) {
|
|||||||
return provider
|
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) {
|
function createStubedProvider (resultStub) {
|
||||||
const engine = createEngineForTestData()
|
const engine = createEngineForTestData()
|
||||||
engine.push(scaffoldMiddleware(resultStub))
|
engine.push(scaffoldMiddleware(resultStub))
|
||||||
|
@ -51,9 +51,8 @@ describe('tx confirmation screen', function () {
|
|||||||
|
|
||||||
actions.cancelTx({value: firstTxId})((action) => {
|
actions.cancelTx({value: firstTxId})((action) => {
|
||||||
result = reducers(initialState, action)
|
result = reducers(initialState, action)
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
|
done()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should transition to the account detail view', function () {
|
it('should transition to the account detail view', function () {
|
||||||
|
@ -3,6 +3,8 @@ const sinon = require('sinon')
|
|||||||
const clone = require('clone')
|
const clone = require('clone')
|
||||||
const MetaMaskController = require('../../app/scripts/metamask-controller')
|
const MetaMaskController = require('../../app/scripts/metamask-controller')
|
||||||
const firstTimeState = require('../../app/scripts/first-time-state')
|
const firstTimeState = require('../../app/scripts/first-time-state')
|
||||||
|
const BN = require('ethereumjs-util').BN
|
||||||
|
const GWEI_BN = new BN('1000000000')
|
||||||
|
|
||||||
describe('MetaMaskController', function () {
|
describe('MetaMaskController', function () {
|
||||||
const noop = () => {}
|
const noop = () => {}
|
||||||
@ -39,17 +41,63 @@ describe('MetaMaskController', function () {
|
|||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
sinon.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
|
sinon.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
|
||||||
|
sinon.spy(metamaskController.keyringController, 'createNewVaultAndRestore')
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
metamaskController.keyringController.createNewVaultAndKeychain.restore()
|
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 () {
|
describe('#createNewVaultAndKeychain', function () {
|
||||||
it('can only create new vault on keyringController once', async function () {
|
it('can only create new vault on keyringController once', async function () {
|
||||||
|
|
||||||
const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity')
|
const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity')
|
||||||
|
|
||||||
|
|
||||||
const password = 'a-fake-password'
|
const password = 'a-fake-password'
|
||||||
|
|
||||||
const first = await metamaskController.createNewVaultAndKeychain(password)
|
const first = await metamaskController.createNewVaultAndKeychain(password)
|
||||||
@ -60,6 +108,22 @@ describe('MetaMaskController', function () {
|
|||||||
selectStub.reset()
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -339,5 +339,64 @@ describe('PendingTransactionTracker', function () {
|
|||||||
|
|
||||||
assert.equal(pendingTxTracker.publishTransaction.callCount, 1, 'Should call publish transaction')
|
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 sinon = require('sinon')
|
||||||
const TransactionController = require('../../app/scripts/controllers/transactions')
|
const TransactionController = require('../../app/scripts/controllers/transactions')
|
||||||
const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils')
|
const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils')
|
||||||
const { createStubedProvider } = require('../stub/provider')
|
const { createStubedProvider, createEthJsQueryStub } = require('../stub/provider')
|
||||||
|
|
||||||
const noop = () => true
|
const noop = () => true
|
||||||
const currentNetworkId = 42
|
const currentNetworkId = 42
|
||||||
@ -30,6 +30,8 @@ describe('Transaction Controller', function () {
|
|||||||
resolve()
|
resolve()
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
txController.query = createEthJsQueryStub(provider)
|
||||||
|
txController.txGasUtil.query = createEthJsQueryStub(provider)
|
||||||
txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop })
|
txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop })
|
||||||
txController.txProviderUtils = new TxGasUtils(txController.provider)
|
txController.txProviderUtils = new TxGasUtils(txController.provider)
|
||||||
})
|
})
|
||||||
@ -110,23 +112,16 @@ describe('Transaction Controller', function () {
|
|||||||
history: [],
|
history: [],
|
||||||
}
|
}
|
||||||
txController.txStateManager._saveTxList([txMeta])
|
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 () {
|
afterEach(function () {
|
||||||
txController.txStateManager._saveTxList([])
|
txController.txStateManager._saveTxList([])
|
||||||
stub.restore()
|
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) {
|
it('should resolve when finished and status is submitted and resolve with the hash', function (done) {
|
||||||
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
||||||
@ -160,8 +155,17 @@ describe('Transaction Controller', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('#addUnapprovedTransaction', 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) {
|
it('should add an unapproved transaction and return a valid txMeta', function (done) {
|
||||||
const addTxDefaultsStub = sinon.stub(txController, 'addTxDefaults').callsFake(() => Promise.resolve())
|
|
||||||
txController.addUnapprovedTransaction({})
|
txController.addUnapprovedTransaction({})
|
||||||
.then((txMeta) => {
|
.then((txMeta) => {
|
||||||
assert(('id' in txMeta), 'should have a id')
|
assert(('id' in txMeta), 'should have a id')
|
||||||
@ -172,10 +176,20 @@ describe('Transaction Controller', function () {
|
|||||||
|
|
||||||
const memTxMeta = txController.txStateManager.getTx(txMeta.id)
|
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}`)
|
assert.deepEqual(txMeta, memTxMeta, `txMeta should be stored in txController after adding it\n expected: ${txMeta} \n got: ${memTxMeta}`)
|
||||||
addTxDefaultsStub.restore()
|
|
||||||
done()
|
done()
|
||||||
}).catch(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 () {
|
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)
|
var output = util.normalizeEthStringToWei(input)
|
||||||
assert.equal(output.toString(10), ethInWei)
|
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 () {
|
describe('#normalizeNumberToWei', function () {
|
||||||
|
@ -2,7 +2,6 @@ const inherits = require('util').inherits
|
|||||||
const Component = require('react').Component
|
const Component = require('react').Component
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('../../actions')
|
|
||||||
import Select from 'react-select'
|
import Select from 'react-select'
|
||||||
|
|
||||||
// Subviews
|
// Subviews
|
||||||
@ -34,37 +33,14 @@ AccountImportSubview.prototype.render = function () {
|
|||||||
const { type } = state
|
const { type } = state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('div.flex-center', {
|
h('div.new-account-import-form', [
|
||||||
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('h3', { style: { padding: '3px' } }, 'SELECT TYPE'),
|
h('div.new-account-import-form__select-section', [
|
||||||
|
|
||||||
h('style', `
|
h('div.new-account-import-form__select-label', 'SELECT TYPE'),
|
||||||
.has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label {
|
|
||||||
color: rgb(174,174,174);
|
|
||||||
}
|
|
||||||
`),
|
|
||||||
|
|
||||||
h(Select, {
|
h(Select, {
|
||||||
|
className: 'new-account-import-form__select',
|
||||||
name: 'import-type-select',
|
name: 'import-type-select',
|
||||||
clearable: false,
|
clearable: false,
|
||||||
value: type || menuItems[0],
|
value: type || menuItems[0],
|
||||||
@ -75,10 +51,10 @@ AccountImportSubview.prototype.render = function () {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
onChange: (opt) => {
|
onChange: (opt) => {
|
||||||
props.dispatch(actions.showImportPage())
|
|
||||||
this.setState({ type: opt.value })
|
this.setState({ type: opt.value })
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
this.renderImportView(),
|
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 abi = require('human-standard-token-abi')
|
||||||
const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url')
|
const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url')
|
||||||
|
const { getTokenAddressFromTokenObject } = require('./util')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
|
||||||
var actions = {
|
var actions = {
|
||||||
@ -50,12 +51,16 @@ var actions = {
|
|||||||
SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED',
|
SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED',
|
||||||
SHOW_INFO_PAGE: 'SHOW_INFO_PAGE',
|
SHOW_INFO_PAGE: 'SHOW_INFO_PAGE',
|
||||||
SHOW_IMPORT_PAGE: 'SHOW_IMPORT_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,
|
unlockMetamask: unlockMetamask,
|
||||||
unlockFailed: unlockFailed,
|
unlockFailed: unlockFailed,
|
||||||
showCreateVault: showCreateVault,
|
showCreateVault: showCreateVault,
|
||||||
showRestoreVault: showRestoreVault,
|
showRestoreVault: showRestoreVault,
|
||||||
showInitializeMenu: showInitializeMenu,
|
showInitializeMenu: showInitializeMenu,
|
||||||
showImportPage,
|
showImportPage,
|
||||||
|
showNewAccountPage,
|
||||||
|
setNewAccountForm,
|
||||||
createNewVaultAndKeychain: createNewVaultAndKeychain,
|
createNewVaultAndKeychain: createNewVaultAndKeychain,
|
||||||
createNewVaultAndRestore: createNewVaultAndRestore,
|
createNewVaultAndRestore: createNewVaultAndRestore,
|
||||||
createNewVaultInProgress: createNewVaultInProgress,
|
createNewVaultInProgress: createNewVaultInProgress,
|
||||||
@ -125,6 +130,7 @@ var actions = {
|
|||||||
sendTx: sendTx,
|
sendTx: sendTx,
|
||||||
signTx: signTx,
|
signTx: signTx,
|
||||||
signTokenTx: signTokenTx,
|
signTokenTx: signTokenTx,
|
||||||
|
updateTransaction,
|
||||||
updateAndApproveTx,
|
updateAndApproveTx,
|
||||||
cancelTx: cancelTx,
|
cancelTx: cancelTx,
|
||||||
completedTx: completedTx,
|
completedTx: completedTx,
|
||||||
@ -244,6 +250,13 @@ var actions = {
|
|||||||
setFeatureFlag,
|
setFeatureFlag,
|
||||||
updateFeatureFlags,
|
updateFeatureFlags,
|
||||||
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
|
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
|
||||||
|
|
||||||
|
// Network
|
||||||
|
setNetworkEndpoints,
|
||||||
|
updateNetworkEndpointType,
|
||||||
|
UPDATE_NETWORK_ENDPOINT_TYPE: 'UPDATE_NETWORK_ENDPOINT_TYPE',
|
||||||
|
|
||||||
|
retryTransaction,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = actions
|
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) {
|
function updateAndApproveTx (txData) {
|
||||||
log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData))
|
log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData))
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
@ -829,6 +859,7 @@ function cancelTx (txData) {
|
|||||||
log.debug(`background.cancelTransaction`)
|
log.debug(`background.cancelTransaction`)
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
background.cancelTransaction(txData.id, () => {
|
background.cancelTransaction(txData.id, () => {
|
||||||
|
dispatch(actions.clearSend())
|
||||||
dispatch(actions.completedTx(txData.id))
|
dispatch(actions.completedTx(txData.id))
|
||||||
resolve(txData)
|
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 () {
|
function createNewVaultInProgress () {
|
||||||
return {
|
return {
|
||||||
type: actions.CREATE_NEW_VAULT_IN_PROGRESS,
|
type: actions.CREATE_NEW_VAULT_IN_PROGRESS,
|
||||||
@ -976,9 +1021,13 @@ function lockMetamask () {
|
|||||||
})
|
})
|
||||||
.then(newState => {
|
.then(newState => {
|
||||||
dispatch(actions.updateMetamaskState(newState))
|
dispatch(actions.updateMetamaskState(newState))
|
||||||
|
dispatch(actions.hideLoadingIndication())
|
||||||
|
dispatch({ type: actions.LOCK_METAMASK })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
dispatch(actions.hideLoadingIndication())
|
||||||
dispatch({ type: actions.LOCK_METAMASK })
|
dispatch({ type: actions.LOCK_METAMASK })
|
||||||
})
|
})
|
||||||
.catch(() => dispatch({ type: actions.LOCK_METAMASK }))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1123,10 +1172,12 @@ function removeToken (address) {
|
|||||||
function addTokens (tokens) {
|
function addTokens (tokens) {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
if (Array.isArray(tokens)) {
|
if (Array.isArray(tokens)) {
|
||||||
|
dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens[0])))
|
||||||
return Promise.all(tokens.map(({ address, symbol, decimals }) => (
|
return Promise.all(tokens.map(({ address, symbol, decimals }) => (
|
||||||
dispatch(addToken(address, symbol, decimals))
|
dispatch(addToken(address, symbol, decimals))
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
|
dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens)))
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
Object
|
Object
|
||||||
.entries(tokens)
|
.entries(tokens)
|
||||||
@ -1196,6 +1247,19 @@ function markAccountsFound () {
|
|||||||
return callBackgroundThenUpdate(background.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
|
// config
|
||||||
//
|
//
|
||||||
@ -1472,7 +1536,6 @@ function pairUpdate (coin) {
|
|||||||
|
|
||||||
function shapeShiftSubview (network) {
|
function shapeShiftSubview (network) {
|
||||||
var pair = 'btc_eth'
|
var pair = 'btc_eth'
|
||||||
|
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(actions.showSubLoadingIndication())
|
dispatch(actions.showSubLoadingIndication())
|
||||||
shapeShiftRequest('marketinfo', {pair}, (mktResponse) => {
|
shapeShiftRequest('marketinfo', {pair}, (mktResponse) => {
|
||||||
@ -1498,7 +1561,7 @@ function coinShiftRquest (data, marketData) {
|
|||||||
dispatch(actions.hideLoadingIndication())
|
dispatch(actions.hideLoadingIndication())
|
||||||
if (response.error) return dispatch(actions.displayWarning(response.error))
|
if (response.error) return dispatch(actions.displayWarning(response.error))
|
||||||
var message = `
|
var message = `
|
||||||
Deposit your ${response.depositType} to the address bellow:`
|
Deposit your ${response.depositType} to the address below:`
|
||||||
log.debug(`background.createShapeShiftTx`)
|
log.debug(`background.createShapeShiftTx`)
|
||||||
background.createShapeShiftTx(response.deposit, response.depositType)
|
background.createShapeShiftTx(response.deposit, response.depositType)
|
||||||
dispatch(actions.showQrView(response.deposit, [message].concat(marketData)))
|
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))
|
if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error))
|
||||||
|
|
||||||
var message = [
|
var message = [
|
||||||
`Deposit your ${coin} to the address bellow:`,
|
`Deposit your ${coin} to the address below:`,
|
||||||
`Deposit Limit: ${mktResponse.limit}`,
|
`Deposit Limit: ${mktResponse.limit}`,
|
||||||
`Deposit Minimum:${mktResponse.minimum}`,
|
`Deposit Minimum:${mktResponse.minimum}`,
|
||||||
]
|
]
|
||||||
@ -1600,10 +1663,7 @@ function updateTokenExchangeRate (token = '') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFeatureFlag (feature, activated) {
|
function setFeatureFlag (feature, activated, notificationType) {
|
||||||
const notificationType = activated
|
|
||||||
? 'BETA_UI_NOTIFICATION_MODAL'
|
|
||||||
: 'OLD_UI_NOTIFICATION_MODAL'
|
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(actions.showLoadingIndication())
|
dispatch(actions.showLoadingIndication())
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -1611,10 +1671,10 @@ function setFeatureFlag (feature, activated) {
|
|||||||
dispatch(actions.hideLoadingIndication())
|
dispatch(actions.hideLoadingIndication())
|
||||||
if (err) {
|
if (err) {
|
||||||
dispatch(actions.displayWarning(err.message))
|
dispatch(actions.displayWarning(err.message))
|
||||||
reject(err)
|
return reject(err)
|
||||||
}
|
}
|
||||||
dispatch(actions.updateFeatureFlags(updatedFeatureFlags))
|
dispatch(actions.updateFeatureFlags(updatedFeatureFlags))
|
||||||
dispatch(actions.showModal({ name: notificationType }))
|
notificationType && dispatch(actions.showModal({ name: notificationType }))
|
||||||
resolve(updatedFeatureFlags)
|
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 { compose } = require('recompose')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
|
const classnames = require('classnames')
|
||||||
|
|
||||||
// mascara
|
// mascara
|
||||||
const MascaraCreatePassword = require('../../mascara/src/app/first-time/create-password-screen').default
|
const MascaraCreatePassword = require('../../mascara/src/app/first-time/create-password-screen').default
|
||||||
const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ether-screen').default
|
const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ether-screen').default
|
||||||
@ -234,22 +236,22 @@ class App extends Component {
|
|||||||
showNetworkDropdown,
|
showNetworkDropdown,
|
||||||
hideNetworkDropdown,
|
hideNetworkDropdown,
|
||||||
currentView,
|
currentView,
|
||||||
isMascara,
|
|
||||||
isOnboarding,
|
|
||||||
history,
|
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
if (window.METAMASK_UI_TYPE === 'notification') {
|
if (window.METAMASK_UI_TYPE === 'notification') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const props = this.props
|
||||||
|
const {isMascara, isOnboarding} = props
|
||||||
|
|
||||||
// Do not render header if user is in mascara onboarding
|
// Do not render header if user is in mascara onboarding
|
||||||
if (isMascara && isOnboarding) {
|
if (isMascara && isOnboarding) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not render header if user is in mascara buy ether
|
// Do not render header if user is in mascara buy ether
|
||||||
if (isMascara && currentView.name === 'buyEth') {
|
if (isMascara && props.currentView.name === 'buyEth') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +262,9 @@ class App extends Component {
|
|||||||
}, [
|
}, [
|
||||||
|
|
||||||
h('.app-header.flex-row.flex-space-between', {
|
h('.app-header.flex-row.flex-space-between', {
|
||||||
style: {},
|
className: classnames({
|
||||||
|
'app-header--initialized': !isOnboarding,
|
||||||
|
}),
|
||||||
}, [
|
}, [
|
||||||
h('div.app-header-contents', {}, [
|
h('div.app-header-contents', {}, [
|
||||||
h('div.left-menu-wrapper', {
|
h('div.left-menu-wrapper', {
|
||||||
@ -268,19 +272,13 @@ class App extends Component {
|
|||||||
}, [
|
}, [
|
||||||
// mini logo
|
// mini logo
|
||||||
h('img.metafox-icon', {
|
h('img.metafox-icon', {
|
||||||
height: 29,
|
height: 42,
|
||||||
width: 29,
|
width: 42,
|
||||||
src: '/images/icon-128.png',
|
src: '/images/metamask-fox.svg',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// metamask name
|
// metamask name
|
||||||
h('h1', {
|
h('h1', 'MetaMask'),
|
||||||
style: {
|
|
||||||
position: 'relative',
|
|
||||||
paddingLeft: '9px',
|
|
||||||
color: '#5B5D67',
|
|
||||||
},
|
|
||||||
}, 'MetaMask'),
|
|
||||||
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -313,6 +311,7 @@ class App extends Component {
|
|||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -33,15 +33,28 @@ function mapDispatchToProps (dispatch) {
|
|||||||
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
|
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
|
||||||
showAccountDetail: address => {
|
showAccountDetail: address => {
|
||||||
dispatch(actions.showAccountDetail(address))
|
dispatch(actions.showAccountDetail(address))
|
||||||
|
dispatch(actions.hideSidebar())
|
||||||
dispatch(actions.toggleAccountMenu())
|
dispatch(actions.toggleAccountMenu())
|
||||||
},
|
},
|
||||||
lockMetamask: () => {
|
lockMetamask: () => {
|
||||||
dispatch(actions.lockMetamask())
|
dispatch(actions.lockMetamask())
|
||||||
dispatch(actions.displayWarning(null))
|
dispatch(actions.hideWarning())
|
||||||
|
dispatch(actions.hideSidebar())
|
||||||
dispatch(actions.toggleAccountMenu())
|
dispatch(actions.toggleAccountMenu())
|
||||||
},
|
},
|
||||||
showNewAccountModal: () => {
|
showConfigPage: () => {
|
||||||
dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
|
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())
|
dispatch(actions.toggleAccountMenu())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -51,7 +64,7 @@ AccountMenu.prototype.render = function () {
|
|||||||
const {
|
const {
|
||||||
isAccountMenuOpen,
|
isAccountMenuOpen,
|
||||||
toggleAccountMenu,
|
toggleAccountMenu,
|
||||||
showNewAccountModal,
|
showNewAccountPage,
|
||||||
lockMetamask,
|
lockMetamask,
|
||||||
history,
|
history,
|
||||||
} = this.props
|
} = this.props
|
||||||
@ -73,15 +86,12 @@ AccountMenu.prototype.render = function () {
|
|||||||
h('div.account-menu__accounts', this.renderAccounts()),
|
h('div.account-menu__accounts', this.renderAccounts()),
|
||||||
h(Divider),
|
h(Divider),
|
||||||
h(Item, {
|
h(Item, {
|
||||||
onClick: showNewAccountModal,
|
onClick: () => showNewAccountPage('CREATE'),
|
||||||
icon: h('img', { src: 'images/plus-btn-white.svg' }),
|
icon: h('img', { src: 'images/plus-btn-white.svg' }),
|
||||||
text: 'Create Account',
|
text: 'Create Account',
|
||||||
}),
|
}),
|
||||||
h(Item, {
|
h(Item, {
|
||||||
onClick: () => {
|
onClick: () => showNewAccountPage('IMPORT'),
|
||||||
toggleAccountMenu()
|
|
||||||
history.push(IMPORT_ACCOUNT_ROUTE)
|
|
||||||
},
|
|
||||||
icon: h('img', { src: 'images/import-account.svg' }),
|
icon: h('img', { src: 'images/import-account.svg' }),
|
||||||
text: 'Import Account',
|
text: 'Import Account',
|
||||||
}),
|
}),
|
||||||
|
@ -40,7 +40,7 @@ BalanceComponent.prototype.render = function () {
|
|||||||
// style: {},
|
// style: {},
|
||||||
// }),
|
// }),
|
||||||
h(Identicon, {
|
h(Identicon, {
|
||||||
diameter: 45,
|
diameter: 50,
|
||||||
address: token && token.address,
|
address: token && token.address,
|
||||||
network,
|
network,
|
||||||
}),
|
}),
|
||||||
@ -94,7 +94,8 @@ BalanceComponent.prototype.renderFiatValue = function (formattedBalance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BalanceComponent.prototype.renderFiatAmount = function (fiatDisplayNumber, fiatSuffix, fiatPrefix) {
|
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', {
|
return h('div.fiat-amount', {
|
||||||
style: {},
|
style: {},
|
||||||
|
@ -40,7 +40,7 @@ CoinbaseForm.prototype.render = function () {
|
|||||||
}, 'Continue to Coinbase'),
|
}, 'Continue to Coinbase'),
|
||||||
|
|
||||||
h('button.btn-red', {
|
h('button.btn-red', {
|
||||||
onClick: () => props.dispatch(actions.backTobuyView(props.accounts.address)),
|
onClick: () => props.dispatch(actions.goHome()),
|
||||||
}, 'Cancel'),
|
}, 'Cancel'),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
@ -50,10 +50,18 @@ function sanitizeValue (value) {
|
|||||||
|
|
||||||
CurrencyInput.prototype.handleChange = function (newValue) {
|
CurrencyInput.prototype.handleChange = function (newValue) {
|
||||||
const { onInputChange } = this.props
|
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
|
// If state.value === props.value plus a decimal point, or at least one
|
||||||
|
@ -199,7 +199,7 @@ class AccountDropdowns extends Component {
|
|||||||
{},
|
{},
|
||||||
menuItemStyles,
|
menuItemStyles,
|
||||||
),
|
),
|
||||||
onClick: () => actions.showNewAccountModal(),
|
onClick: () => actions.showNewAccountPageCreateForm(),
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
h(
|
h(
|
||||||
@ -228,7 +228,7 @@ class AccountDropdowns extends Component {
|
|||||||
actions.hideSidebar()
|
actions.hideSidebar()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick: () => actions.showImportPage(),
|
onClick: () => actions.showNewAccountPageImportForm(),
|
||||||
style: Object.assign(
|
style: Object.assign(
|
||||||
{},
|
{},
|
||||||
menuItemStyles,
|
menuItemStyles,
|
||||||
@ -457,9 +457,7 @@ const mapDispatchToProps = (dispatch) => {
|
|||||||
identity,
|
identity,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
showNewAccountModal: () => {
|
showNewAccountPageCreateForm: () => dispatch(actions.showNewAccountPage({ form: 'CREATE' })),
|
||||||
dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
|
|
||||||
},
|
|
||||||
showExportPrivateKeyModal: () => {
|
showExportPrivateKeyModal: () => {
|
||||||
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
|
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
|
||||||
},
|
},
|
||||||
@ -467,7 +465,7 @@ const mapDispatchToProps = (dispatch) => {
|
|||||||
dispatch(actions.showAddTokenPage())
|
dispatch(actions.showAddTokenPage())
|
||||||
},
|
},
|
||||||
addNewAccount: () => dispatch(actions.addNewAccount()),
|
addNewAccount: () => dispatch(actions.addNewAccount()),
|
||||||
showImportPage: () => dispatch(actions.showImportPage()),
|
showNewAccountPageImportForm: () => dispatch(actions.showNewAccountPage({ form: 'IMPORT' })),
|
||||||
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
|
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,13 @@ const debounce = require('debounce')
|
|||||||
module.exports = Mascot
|
module.exports = Mascot
|
||||||
|
|
||||||
inherits(Mascot, Component)
|
inherits(Mascot, Component)
|
||||||
function Mascot () {
|
function Mascot ({width = '200', height = '200'}) {
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
this.logo = metamaskLogo({
|
this.logo = metamaskLogo({
|
||||||
followMouse: true,
|
followMouse: true,
|
||||||
pxNotRatio: true,
|
pxNotRatio: true,
|
||||||
width: 200,
|
width,
|
||||||
height: 200,
|
height,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000)
|
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('div.account-modal-divider'),
|
||||||
|
|
||||||
h('button.btn-clear', {
|
h('button.btn-clear.account-modal__button', {
|
||||||
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
|
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
|
||||||
}, 'View account on Etherscan'),
|
}, 'View account on Etherscan'),
|
||||||
|
|
||||||
// Holding on redesign for Export Private Key functionality
|
// Holding on redesign for Export Private Key functionality
|
||||||
h('button.btn-clear', {
|
h('button.btn-clear.account-modal__button', {
|
||||||
onClick: () => showExportPrivateKeyModal(),
|
onClick: () => showExportPrivateKeyModal(),
|
||||||
}, 'Export private key'),
|
}, 'Export private key'),
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ BuyOptions.prototype.render = function () {
|
|||||||
// h('div.buy-modal-content-option', {}, [
|
// h('div.buy-modal-content-option', {}, [
|
||||||
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
|
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
|
||||||
// h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'),
|
// h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'),
|
||||||
// ]),
|
// ]),,
|
||||||
|
|
||||||
this.renderModalContentOption(
|
this.renderModalContentOption(
|
||||||
'Direct Deposit',
|
'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) {
|
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) {
|
||||||
return h('div.export-private-key-buttons', {}, [
|
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
|
(privateKey
|
||||||
? this.renderButton('btn-clear', () => hideModal(), 'Done')
|
? this.renderButton('btn-clear export-private-key__button', () => hideModal(), 'Done')
|
||||||
: this.renderButton('btn-clear', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Show')
|
: 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('div.hide-token-confirmation__buttons', {}, [
|
||||||
h('button.btn-clear', {
|
h('button.btn-cancel.hide-token-confirmation__button', {
|
||||||
onClick: () => hideModal(),
|
onClick: () => hideModal(),
|
||||||
}, [
|
}, [
|
||||||
'CANCEL',
|
'CANCEL',
|
||||||
]),
|
]),
|
||||||
h('button.btn-clear', {
|
h('button.btn-clear.hide-token-confirmation__button', {
|
||||||
onClick: () => hideToken(address),
|
onClick: () => hideToken(address),
|
||||||
}, [
|
}, [
|
||||||
'HIDE',
|
'HIDE',
|
||||||
|
@ -9,6 +9,7 @@ const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-n
|
|||||||
|
|
||||||
// Modal Components
|
// Modal Components
|
||||||
const BuyOptions = require('./buy-options-modal')
|
const BuyOptions = require('./buy-options-modal')
|
||||||
|
const DepositEtherModal = require('./deposit-ether-modal')
|
||||||
const AccountDetailsModal = require('./account-details-modal')
|
const AccountDetailsModal = require('./account-details-modal')
|
||||||
const EditAccountNameModal = require('./edit-account-name-modal')
|
const EditAccountNameModal = require('./edit-account-name-modal')
|
||||||
const ExportPrivateKeyModal = require('./export-private-key-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: {
|
EDIT_ACCOUNT_NAME: {
|
||||||
contents: [
|
contents: [
|
||||||
h(EditAccountNameModal, {}, []),
|
h(EditAccountNameModal, {}, []),
|
||||||
|
@ -39,7 +39,6 @@ Network.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
src: 'images/loading.svg',
|
src: 'images/loading.svg',
|
||||||
}),
|
}),
|
||||||
h('i.fa.fa-caret-down.network-caret'),
|
|
||||||
])
|
])
|
||||||
} else if (providerName === 'mainnet') {
|
} else if (providerName === 'mainnet') {
|
||||||
hoverText = 'Main Ethereum Network'
|
hoverText = 'Main Ethereum Network'
|
||||||
@ -85,12 +84,8 @@ Network.prototype.render = function () {
|
|||||||
backgroundColor: '#038789', // $blue-lagoon
|
backgroundColor: '#038789', // $blue-lagoon
|
||||||
nonSelectBackgroundColor: '#15afb2',
|
nonSelectBackgroundColor: '#15afb2',
|
||||||
}),
|
}),
|
||||||
h('.network-name', {
|
h('.network-name', 'Main Network'),
|
||||||
style: {
|
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||||
color: '#039396',
|
|
||||||
}},
|
|
||||||
'Main Network'),
|
|
||||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
|
||||||
])
|
])
|
||||||
case 'ropsten-test-network':
|
case 'ropsten-test-network':
|
||||||
return h('.network-indicator', [
|
return h('.network-indicator', [
|
||||||
@ -98,12 +93,8 @@ Network.prototype.render = function () {
|
|||||||
backgroundColor: '#e91550', // $crimson
|
backgroundColor: '#e91550', // $crimson
|
||||||
nonSelectBackgroundColor: '#ec2c50',
|
nonSelectBackgroundColor: '#ec2c50',
|
||||||
}),
|
}),
|
||||||
h('.network-name', {
|
h('.network-name', 'Ropsten Test Net'),
|
||||||
style: {
|
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||||
color: '#ff6666',
|
|
||||||
}},
|
|
||||||
'Ropsten Test Net'),
|
|
||||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
|
||||||
])
|
])
|
||||||
case 'kovan-test-network':
|
case 'kovan-test-network':
|
||||||
return h('.network-indicator', [
|
return h('.network-indicator', [
|
||||||
@ -111,12 +102,8 @@ Network.prototype.render = function () {
|
|||||||
backgroundColor: '#690496', // $purple
|
backgroundColor: '#690496', // $purple
|
||||||
nonSelectBackgroundColor: '#b039f3',
|
nonSelectBackgroundColor: '#b039f3',
|
||||||
}),
|
}),
|
||||||
h('.network-name', {
|
h('.network-name', 'Kovan Test Net'),
|
||||||
style: {
|
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||||
color: '#690496',
|
|
||||||
}},
|
|
||||||
'Kovan Test Net'),
|
|
||||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
|
||||||
])
|
])
|
||||||
case 'rinkeby-test-network':
|
case 'rinkeby-test-network':
|
||||||
return h('.network-indicator', [
|
return h('.network-indicator', [
|
||||||
@ -124,12 +111,8 @@ Network.prototype.render = function () {
|
|||||||
backgroundColor: '#ebb33f', // $tulip-tree
|
backgroundColor: '#ebb33f', // $tulip-tree
|
||||||
nonSelectBackgroundColor: '#ecb23e',
|
nonSelectBackgroundColor: '#ecb23e',
|
||||||
}),
|
}),
|
||||||
h('.network-name', {
|
h('.network-name', 'Rinkeby Test Net'),
|
||||||
style: {
|
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||||
color: '#e7a218',
|
|
||||||
}},
|
|
||||||
'Rinkeby Test Net'),
|
|
||||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
|
||||||
])
|
])
|
||||||
default:
|
default:
|
||||||
return h('.network-indicator', [
|
return h('.network-indicator', [
|
||||||
@ -140,12 +123,8 @@ Network.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('.network-name', {
|
h('.network-name', 'Private Network'),
|
||||||
style: {
|
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||||
color: '#AEAEAE',
|
|
||||||
}},
|
|
||||||
'Private Network'),
|
|
||||||
h('i.fa.fa-caret-down.fa-lg.network-caret'),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
})(),
|
})(),
|
||||||
|
@ -3,6 +3,7 @@ const Component = require('react').Component
|
|||||||
const classnames = require('classnames')
|
const classnames = require('classnames')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
|
const R = require('ramda')
|
||||||
const Fuse = require('fuse.js')
|
const Fuse = require('fuse.js')
|
||||||
const contractMap = require('eth-contract-metadata')
|
const contractMap = require('eth-contract-metadata')
|
||||||
const TokenBalance = require('../../components/token-balance')
|
const TokenBalance = require('../../components/token-balance')
|
||||||
@ -17,7 +18,10 @@ const fuse = new Fuse(contractList, {
|
|||||||
distance: 100,
|
distance: 100,
|
||||||
maxPatternLength: 32,
|
maxPatternLength: 32,
|
||||||
minMatchCharLength: 1,
|
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')
|
||||||
const actions = require('../../actions')
|
const actions = require('../../actions')
|
||||||
@ -219,9 +223,11 @@ AddTokenScreen.prototype.renderCustomForm = function () {
|
|||||||
|
|
||||||
AddTokenScreen.prototype.renderTokenList = function () {
|
AddTokenScreen.prototype.renderTokenList = function () {
|
||||||
const { searchQuery = '', selectedTokens } = this.state
|
const { searchQuery = '', selectedTokens } = this.state
|
||||||
const results = searchQuery
|
const fuseSearchResult = fuse.search(searchQuery)
|
||||||
? fuse.search(searchQuery) || []
|
const addressSearchResult = contractList.filter(token => {
|
||||||
: contractList
|
return token.address.toLowerCase() === searchQuery.toLowerCase()
|
||||||
|
})
|
||||||
|
const results = [...addressSearchResult, ...fuseSearchResult]
|
||||||
|
|
||||||
return Array(6).fill(undefined)
|
return Array(6).fill(undefined)
|
||||||
.map((_, i) => {
|
.map((_, i) => {
|
||||||
@ -297,12 +303,12 @@ AddTokenScreen.prototype.renderConfirmation = function () {
|
|||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
h('div.add-token__buttons', [
|
h('div.add-token__buttons', [
|
||||||
h('button.btn-secondary', {
|
h('button.btn-cancel.add-token__button', {
|
||||||
onClick: () => addTokens(tokens).then(() => history.push(DEFAULT_ROUTE)),
|
|
||||||
}, 'Add Tokens'),
|
|
||||||
h('button.btn-tertiary', {
|
|
||||||
onClick: () => this.setState({ isShowingConfirmation: false }),
|
onClick: () => this.setState({ isShowingConfirmation: false }),
|
||||||
}, 'Back'),
|
}, '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('div.add-token__buttons', [
|
||||||
h('button.btn-secondary', {
|
h('button.btn-cancel.add-token__button', {
|
||||||
onClick: this.onNext,
|
|
||||||
}, 'Next'),
|
|
||||||
h('button.btn-tertiary', {
|
|
||||||
onClick: () => history.goBack(),
|
onClick: () => history.goBack(),
|
||||||
}, 'Cancel'),
|
}, 'Cancel'),
|
||||||
|
h('button.btn-clear.add-token__button', {
|
||||||
|
onClick: this.onNext,
|
||||||
|
}, 'Next'),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
@ -24,14 +24,7 @@ JsonImportSubview.prototype.render = function () {
|
|||||||
const { error } = this.props
|
const { error } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('div', {
|
h('div.new-account-import-form__json', [
|
||||||
style: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: '5px 15px 0px 15px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
|
|
||||||
h('p', 'Used by a variety of different clients'),
|
h('p', 'Used by a variety of different clients'),
|
||||||
h('a.warning', { href: HELP_LINK, target: '_blank' }, 'File import not working? Click here!'),
|
h('a.warning', { href: HELP_LINK, target: '_blank' }, 'File import not working? Click here!'),
|
||||||
@ -40,28 +33,35 @@ JsonImportSubview.prototype.render = function () {
|
|||||||
readAs: 'text',
|
readAs: 'text',
|
||||||
onLoad: this.onLoad.bind(this),
|
onLoad: this.onLoad.bind(this),
|
||||||
style: {
|
style: {
|
||||||
margin: '20px 0px 12px 20px',
|
margin: '20px 0px 12px 34%',
|
||||||
fontSize: '15px',
|
fontSize: '15px',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('input.large-input.letter-spacey', {
|
h('input.new-account-import-form__input-password', {
|
||||||
type: 'password',
|
type: 'password',
|
||||||
placeholder: 'Enter password',
|
placeholder: 'Enter password',
|
||||||
id: 'json-password-box',
|
id: 'json-password-box',
|
||||||
onKeyPress: this.createKeyringOnEnter.bind(this),
|
onKeyPress: this.createKeyringOnEnter.bind(this),
|
||||||
style: {
|
|
||||||
width: 260,
|
|
||||||
marginTop: 12,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('button.primary', {
|
h('div.new-account-create-form__buttons', {}, [
|
||||||
onClick: this.createNewKeychain.bind(this),
|
|
||||||
style: {
|
h('button.new-account-create-form__button-cancel', {
|
||||||
margin: 12,
|
onClick: () => this.props.goHome(),
|
||||||
},
|
}, [
|
||||||
}, 'Import'),
|
'CANCEL',
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('button.new-account-create-form__button-create', {
|
||||||
|
onClick: () => this.createNewKeychain.bind(this),
|
||||||
|
}, [
|
||||||
|
'IMPORT',
|
||||||
|
]),
|
||||||
|
|
||||||
|
]),
|
||||||
|
|
||||||
error ? h('span.error', error) : null,
|
error ? h('span.error', error) : null,
|
||||||
])
|
])
|
||||||
|
@ -4,7 +4,7 @@ const h = require('react-hyperscript')
|
|||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('../../../actions')
|
const actions = require('../../../actions')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(PrivateKeyImportView)
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(PrivateKeyImportView)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
return {
|
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)
|
inherits(PrivateKeyImportView, Component)
|
||||||
function PrivateKeyImportView () {
|
function PrivateKeyImportView () {
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivateKeyImportView.prototype.componentWillUnmount = function () {
|
|
||||||
this.props.dispatch(actions.displayWarning(null))
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateKeyImportView.prototype.render = function () {
|
PrivateKeyImportView.prototype.render = function () {
|
||||||
const { error } = this.props
|
const { error, goHome } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('div', {
|
h('div.new-account-import-form__private-key', [
|
||||||
style: {
|
h('span.new-account-create-form__instruction', 'Paste your private key string here:'),
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: '5px 15px 0px 15px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('span', 'Paste your private key string here'),
|
|
||||||
|
|
||||||
h('input.large-input.letter-spacey', {
|
h('input.new-account-import-form__input-password', {
|
||||||
type: 'password',
|
type: 'password',
|
||||||
id: 'private-key-box',
|
id: 'private-key-box',
|
||||||
onKeyPress: this.createKeyringOnEnter.bind(this),
|
onKeyPress: () => this.createKeyringOnEnter(),
|
||||||
style: {
|
|
||||||
width: 260,
|
|
||||||
marginTop: 12,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('button.primary', {
|
h('div.new-account-create-form__buttons', {}, [
|
||||||
onClick: this.createNewKeychain.bind(this),
|
|
||||||
style: {
|
h('button.new-account-create-form__button-cancel', {
|
||||||
margin: 12,
|
onClick: () => goHome(),
|
||||||
},
|
}, [
|
||||||
}, 'Import'),
|
'CANCEL',
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('button.new-account-create-form__button-create', {
|
||||||
|
onClick: () => this.createNewKeychain(),
|
||||||
|
}, [
|
||||||
|
'IMPORT',
|
||||||
|
]),
|
||||||
|
|
||||||
|
]),
|
||||||
|
|
||||||
error ? h('span.error', error) : null,
|
error ? h('span.error', error) : null,
|
||||||
])
|
])
|
||||||
@ -67,5 +71,6 @@ PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
|
|||||||
PrivateKeyImportView.prototype.createNewKeychain = function () {
|
PrivateKeyImportView.prototype.createNewKeychain = function () {
|
||||||
const input = document.getElementById('private-key-box')
|
const input = document.getElementById('private-key-box')
|
||||||
const privateKey = input.value
|
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
|
// submit
|
||||||
this.props.createNewVaultAndRestore(password, seed)
|
this.props.createNewVaultAndRestore(password, seed)
|
||||||
.then(() => history.push(DEFAULT_ROUTE))
|
.then(() => history.push(DEFAULT_ROUTE))
|
||||||
.catch(({ message }) => this.setState({ error: message }))
|
.catch(({ message }) => {
|
||||||
|
this.setState({ error: message })
|
||||||
|
log.error(message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
@ -11,6 +11,7 @@ const { exportAsFile } = require('../../../util')
|
|||||||
const SimpleDropdown = require('../../dropdowns/simple-dropdown')
|
const SimpleDropdown = require('../../dropdowns/simple-dropdown')
|
||||||
const ToggleButton = require('react-toggle-button')
|
const ToggleButton = require('react-toggle-button')
|
||||||
const { REVEAL_SEED_ROUTE } = require('../../../routes')
|
const { REVEAL_SEED_ROUTE } = require('../../../routes')
|
||||||
|
const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
|
||||||
|
|
||||||
const getInfuraCurrencyOptions = () => {
|
const getInfuraCurrencyOptions = () => {
|
||||||
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
|
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
|
||||||
@ -230,18 +231,18 @@ class Settings extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { warning } = this.props
|
const { warning, isMascara } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('div.settings__content', [
|
h('div.settings__content', [
|
||||||
warning && h('div.settings__error', warning),
|
warning && h('div.settings__error', warning),
|
||||||
this.renderBlockieOptIn(),
|
|
||||||
this.renderCurrentConversion(),
|
this.renderCurrentConversion(),
|
||||||
// this.renderCurrentProvider(),
|
// this.renderCurrentProvider(),
|
||||||
this.renderNewRpcUrl(),
|
this.renderNewRpcUrl(),
|
||||||
this.renderStateLogs(),
|
this.renderStateLogs(),
|
||||||
this.renderSeedWords(),
|
this.renderSeedWords(),
|
||||||
this.renderOldUI(),
|
!isMascara && this.renderOldUI(),
|
||||||
|
this.renderBlockieOptIn(),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -257,12 +258,14 @@ Settings.propTypes = {
|
|||||||
setFeatureFlagToBeta: PropTypes.func,
|
setFeatureFlagToBeta: PropTypes.func,
|
||||||
warning: PropTypes.string,
|
warning: PropTypes.string,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
|
isMascara: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
metamask: state.metamask,
|
metamask: state.metamask,
|
||||||
warning: state.appState.warning,
|
warning: state.appState.warning,
|
||||||
|
isMascara: state.metamask.isMascara,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +276,10 @@ const mapDispatchToProps = dispatch => {
|
|||||||
displayWarning: warning => dispatch(actions.displayWarning(warning)),
|
displayWarning: warning => dispatch(actions.displayWarning(warning)),
|
||||||
revealSeedConfirmation: () => dispatch(actions.revealSeedConfirmation()),
|
revealSeedConfirmation: () => dispatch(actions.revealSeedConfirmation()),
|
||||||
setUseBlockie: value => dispatch(actions.setUseBlockie(value)),
|
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
|
// Main Send token Card
|
||||||
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
||||||
h('h3.flex-center.confirm-screen-header', [
|
h('h3.flex-center.confirm-screen-header', [
|
||||||
h('button.confirm-screen-back-button', {
|
h('button.btn-clear.confirm-screen-back-button', {
|
||||||
onClick: () => this.editTransaction(txMeta),
|
onClick: () => editTransaction(txMeta),
|
||||||
}, 'EDIT'),
|
}, 'EDIT'),
|
||||||
h('div.confirm-screen-title', 'Confirm Transaction'),
|
h('div.confirm-screen-title', 'Confirm Transaction'),
|
||||||
h('div.confirm-screen-header-tip'),
|
h('div.confirm-screen-header-tip'),
|
||||||
@ -433,7 +433,9 @@ ConfirmSendEther.prototype.onSubmit = function (event) {
|
|||||||
|
|
||||||
ConfirmSendEther.prototype.cancel = function (event, txMeta) {
|
ConfirmSendEther.prototype.cancel = function (event, txMeta) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.props.cancelTransaction(txMeta)
|
const { cancelTransaction } = this.props
|
||||||
|
|
||||||
|
cancelTransaction(txMeta)
|
||||||
.then(() => this.props.history.push(DEFAULT_ROUTE))
|
.then(() => this.props.history.push(DEFAULT_ROUTE))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,26 +460,6 @@ ConfirmSendEther.prototype.gatherTxMeta = function () {
|
|||||||
const state = this.state
|
const state = this.state
|
||||||
const txData = clone(state.txData) || clone(props.txData)
|
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)}`)
|
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
||||||
return txData
|
return txData
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ const { withRouter } = require('react-router-dom')
|
|||||||
const { compose } = require('recompose')
|
const { compose } = require('recompose')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const ethAbi = require('ethereumjs-abi')
|
|
||||||
const tokenAbi = require('human-standard-token-abi')
|
const tokenAbi = require('human-standard-token-abi')
|
||||||
const abiDecoder = require('abi-decoder')
|
const abiDecoder = require('abi-decoder')
|
||||||
abiDecoder.addABI(tokenAbi)
|
abiDecoder.addABI(tokenAbi)
|
||||||
@ -305,6 +304,7 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConfirmSendToken.prototype.render = function () {
|
ConfirmSendToken.prototype.render = function () {
|
||||||
|
const { editTransaction } = this.props
|
||||||
const txMeta = this.gatherTxMeta()
|
const txMeta = this.gatherTxMeta()
|
||||||
const {
|
const {
|
||||||
from: {
|
from: {
|
||||||
@ -326,8 +326,8 @@ ConfirmSendToken.prototype.render = function () {
|
|||||||
// Main Send token Card
|
// Main Send token Card
|
||||||
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
||||||
h('h3.flex-center.confirm-screen-header', [
|
h('h3.flex-center.confirm-screen-header', [
|
||||||
h('button.confirm-screen-back-button', {
|
h('button.btn-clear.confirm-screen-back-button', {
|
||||||
onClick: () => this.editTransaction(txMeta),
|
onClick: () => editTransaction(txMeta),
|
||||||
}, 'EDIT'),
|
}, 'EDIT'),
|
||||||
h('div.confirm-screen-title', 'Confirm Transaction'),
|
h('div.confirm-screen-title', 'Confirm Transaction'),
|
||||||
h('div.confirm-screen-header-tip'),
|
h('div.confirm-screen-header-tip'),
|
||||||
@ -426,7 +426,9 @@ ConfirmSendToken.prototype.onSubmit = function (event) {
|
|||||||
|
|
||||||
ConfirmSendToken.prototype.cancel = function (event, txMeta) {
|
ConfirmSendToken.prototype.cancel = function (event, txMeta) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.props.cancelTransaction(txMeta)
|
const { cancelTransaction } = this.props
|
||||||
|
|
||||||
|
cancelTransaction(txMeta)
|
||||||
.then(() => this.props.history.push(DEFAULT_ROUTE))
|
.then(() => this.props.history.push(DEFAULT_ROUTE))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,39 +453,6 @@ ConfirmSendToken.prototype.gatherTxMeta = function () {
|
|||||||
const state = this.state
|
const state = this.state
|
||||||
const txData = clone(state.txData) || clone(props.txData)
|
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)}`)
|
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
||||||
return txData
|
return txData
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,9 @@ GasFeeDisplay.prototype.render = function () {
|
|||||||
})
|
})
|
||||||
: h('div.currency-display', 'Loading...'),
|
: h('div.currency-display', 'Loading...'),
|
||||||
|
|
||||||
h('div.send-v2__sliders-icon-container', {
|
h('button.send-v2__sliders-icon-container', {
|
||||||
onClick,
|
onClick,
|
||||||
|
disabled: !gasTotal,
|
||||||
}, [
|
}, [
|
||||||
h('i.fa.fa-sliders.send-v2__sliders-icon'),
|
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,
|
multiplierBase: 16,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const TOKEN_TRANSFER_FUNCTION_SIGNATURE = '0xa9059cbb'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
MIN_GAS_PRICE_GWEI,
|
MIN_GAS_PRICE_GWEI,
|
||||||
MIN_GAS_PRICE_HEX,
|
MIN_GAS_PRICE_HEX,
|
||||||
@ -27,4 +29,5 @@ module.exports = {
|
|||||||
MIN_GAS_LIMIT_HEX,
|
MIN_GAS_LIMIT_HEX,
|
||||||
MIN_GAS_LIMIT_DEC,
|
MIN_GAS_LIMIT_DEC,
|
||||||
MIN_GAS_TOTAL,
|
MIN_GAS_TOTAL,
|
||||||
|
TOKEN_TRANSFER_FUNCTION_SIGNATURE,
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,8 @@ function mapStateToProps (state) {
|
|||||||
data,
|
data,
|
||||||
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
|
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
|
||||||
tokenContract: getSelectedTokenContract(state),
|
tokenContract: getSelectedTokenContract(state),
|
||||||
|
unapprovedTxs: state.metamask.unapprovedTxs,
|
||||||
|
network: state.metamask.network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +71,7 @@ function mapDispatchToProps (dispatch) {
|
|||||||
),
|
),
|
||||||
signTx: txParams => dispatch(actions.signTx(txParams)),
|
signTx: txParams => dispatch(actions.signTx(txParams)),
|
||||||
updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)),
|
updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)),
|
||||||
|
updateTx: txData => dispatch(actions.updateTransaction(txData)),
|
||||||
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
|
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
|
||||||
addToAddressBook: address => dispatch(actions.addToAddressBook(address)),
|
addToAddressBook: address => dispatch(actions.addToAddressBook(address)),
|
||||||
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
|
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
|
||||||
@ -82,7 +85,6 @@ function mapDispatchToProps (dispatch) {
|
|||||||
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
|
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
|
||||||
goHome: () => dispatch(actions.goHome()),
|
goHome: () => dispatch(actions.goHome()),
|
||||||
clearSend: () => dispatch(actions.clearSend()),
|
clearSend: () => dispatch(actions.clearSend()),
|
||||||
backToConfirmScreen: editingTransactionId => dispatch(actions.showConfTxPage({ id: editingTransactionId })),
|
|
||||||
setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)),
|
setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,308 +1,242 @@
|
|||||||
const PersistentForm = require('../../lib/persistent-form')
|
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
|
const Component = require('react').Component
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('../actions')
|
const classnames = require('classnames')
|
||||||
const Qr = require('./qr-code')
|
const { qrcode } = require('qrcode-npm')
|
||||||
const isValidAddress = require('../util').isValidAddress
|
const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions')
|
||||||
module.exports = connect(mapStateToProps)(ShapeshiftForm)
|
const { isValidAddress } = require('../util')
|
||||||
|
const SimpleDropdown = require('./dropdowns/simple-dropdown')
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
const {
|
||||||
|
coinOptions,
|
||||||
|
tokenExchangeRates,
|
||||||
|
selectedAddress,
|
||||||
|
} = state.metamask
|
||||||
|
|
||||||
return {
|
return {
|
||||||
warning: state.appState.warning,
|
coinOptions,
|
||||||
isSubLoading: state.appState.isSubLoading,
|
tokenExchangeRates,
|
||||||
qrRequested: state.appState.qrRequested,
|
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 () {
|
function ShapeshiftForm () {
|
||||||
PersistentForm.call(this)
|
Component.call(this)
|
||||||
this.persistentFormParentId = 'shapeshift-buy-form'
|
|
||||||
|
this.state = {
|
||||||
|
depositCoin: 'btc',
|
||||||
|
refundAddress: '',
|
||||||
|
showQrCode: false,
|
||||||
|
depositAddress: '',
|
||||||
|
errorMessage: '',
|
||||||
|
isLoading: false,
|
||||||
|
bought: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeshiftForm.prototype.render = function () {
|
ShapeshiftForm.prototype.componentWillMount = function () {
|
||||||
return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain()
|
this.props.shapeShiftSubview()
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeshiftForm.prototype.renderMain = function () {
|
ShapeshiftForm.prototype.onCoinChange = function (e) {
|
||||||
const marketinfo = this.props.buyView.formView.marketinfo
|
const coin = e.target.value
|
||||||
const coinOptions = this.props.buyView.formView.coinOptions
|
this.setState({
|
||||||
var coin = marketinfo.pair.split('_')[0].toUpperCase()
|
depositCoin: coin,
|
||||||
|
errorMessage: '',
|
||||||
return h('.flex-column', {
|
})
|
||||||
style: {
|
this.props.pairUpdate(coin)
|
||||||
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.renderRefundAddressForCoin = function (coin) {
|
ShapeshiftForm.prototype.onBuyWithShapeShift = function () {
|
||||||
return h(this.activeToggle('.input-container'), {
|
this.setState({
|
||||||
style: {
|
isLoading: true,
|
||||||
marginTop: '1%',
|
showQrCode: true,
|
||||||
},
|
})
|
||||||
}, [
|
|
||||||
|
|
||||||
h('div', `${coin} Address:`),
|
const {
|
||||||
|
buyWithShapeShift,
|
||||||
h('input#fromCoinAddress.buy-inputs', {
|
selectedAddress: withdrawal,
|
||||||
type: 'text',
|
} = this.props
|
||||||
placeholder: `Your ${coin} Refund Address`,
|
const {
|
||||||
dataset: {
|
refundAddress: returnAddress,
|
||||||
persistentFormId: 'refund-address',
|
depositCoin,
|
||||||
|
} = this.state
|
||||||
},
|
const pair = `${depositCoin}_eth`
|
||||||
style: {
|
const data = {
|
||||||
boxSizing: 'border-box',
|
withdrawal,
|
||||||
width: '227px',
|
pair,
|
||||||
height: '30px',
|
returnAddress,
|
||||||
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,
|
|
||||||
// Public api key
|
// Public api key
|
||||||
'apiKey': '803d1f5df2ed1b1476e4b9e6bcd089e34d8874595dda6a23b67d93c56ea9cc2445e98a6748b219b2b6ad654d9f075f1f1db139abfa93158c04e825db122c14b6',
|
'apiKey': '803d1f5df2ed1b1476e4b9e6bcd089e34d8874595dda6a23b67d93c56ea9cc2445e98a6748b219b2b6ad654d9f075f1f1db139abfa93158c04e825db122c14b6',
|
||||||
}
|
}
|
||||||
var message = [
|
|
||||||
`Deposit Limit: ${props.buyView.formView.marketinfo.limit}`,
|
|
||||||
`Deposit Minimum:${props.buyView.formView.marketinfo.minimum}`,
|
|
||||||
]
|
|
||||||
if (isValidAddress(withdrawal)) {
|
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 () {
|
ShapeshiftForm.prototype.renderMetadata = function (label, value) {
|
||||||
var list = Object.keys(this.props.buyView.formView.coinOptions).map((item) => {
|
return h('div', {className: 'shapeshift-form__metadata-wrapper'}, [
|
||||||
return h('option', {
|
|
||||||
value: item,
|
|
||||||
}, item)
|
|
||||||
})
|
|
||||||
|
|
||||||
return h('datalist#coinList', {
|
h('div.shapeshift-form__metadata-label', {}, [
|
||||||
onClick: (event) => {
|
h('span', `${label}:`),
|
||||||
event.preventDefault()
|
]),
|
||||||
},
|
|
||||||
}, list)
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.updateCoin = function (event) {
|
h('div.shapeshift-form__metadata-value', {}, [
|
||||||
event.preventDefault()
|
h('span', value),
|
||||||
const props = this.props
|
]),
|
||||||
var coinOptions = this.props.buyView.formView.coinOptions
|
|
||||||
var coin = document.getElementById('fromCoin').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) {
|
ShapeshiftForm.prototype.renderMarketInfo = function () {
|
||||||
if (!this.props.buyView.formView.response || this.props.warning) return elementType
|
const { depositCoin } = this.state
|
||||||
return `${elementType}.inactive`
|
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',
|
|
||||||
},
|
|
||||||
src: 'images/loading.svg',
|
|
||||||
}),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
function mapStateToProps (state) {
|
||||||
return {
|
return {
|
||||||
|
selectedAddress: state.metamask.selectedAddress,
|
||||||
conversionRate: state.metamask.conversionRate,
|
conversionRate: state.metamask.conversionRate,
|
||||||
currentCurrency: state.metamask.currentCurrency,
|
currentCurrency: state.metamask.currentCurrency,
|
||||||
}
|
}
|
||||||
@ -28,36 +29,39 @@ function ShiftListItem () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShiftListItem.prototype.render = function () {
|
ShiftListItem.prototype.render = function () {
|
||||||
|
const { selectedAddress, receivingAddress } = this.props
|
||||||
return (
|
return (
|
||||||
h('div.tx-list-item.tx-list-clickable', {
|
selectedAddress === receivingAddress
|
||||||
style: {
|
? h('div.tx-list-item.tx-list-clickable', {
|
||||||
paddingTop: '20px',
|
|
||||||
paddingBottom: '20px',
|
|
||||||
justifyContent: 'space-around',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
style: {
|
style: {
|
||||||
width: '0px',
|
paddingTop: '20px',
|
||||||
position: 'relative',
|
paddingBottom: '20px',
|
||||||
bottom: '19px',
|
justifyContent: 'space-around',
|
||||||
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
h('img', {
|
h('div', {
|
||||||
src: 'https://info.shapeshift.io/sites/default/files/logo.png',
|
|
||||||
style: {
|
style: {
|
||||||
height: '35px',
|
width: '0px',
|
||||||
width: '132px',
|
position: 'relative',
|
||||||
position: 'absolute',
|
bottom: '19px',
|
||||||
clip: 'rect(0px,23px,34px,0px)',
|
|
||||||
},
|
},
|
||||||
}),
|
}, [
|
||||||
]),
|
h('img', {
|
||||||
|
src: 'https://info.shapeshift.io/sites/default/files/logo.png',
|
||||||
|
style: {
|
||||||
|
height: '35px',
|
||||||
|
width: '132px',
|
||||||
|
position: 'absolute',
|
||||||
|
clip: 'rect(0px,23px,34px,0px)',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
this.renderInfo(),
|
this.renderInfo(),
|
||||||
this.renderUtilComponents(),
|
this.renderUtilComponents(),
|
||||||
])
|
])
|
||||||
|
: null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,9 @@ TokenCell.prototype.render = function () {
|
|||||||
numberOfDecimals: 2,
|
numberOfDecimals: 2,
|
||||||
conversionRate: currentTokenToFiatRate,
|
conversionRate: currentTokenToFiatRate,
|
||||||
})
|
})
|
||||||
formattedFiat = `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
|
formattedFiat = currentTokenInFiat.toString() === '0'
|
||||||
|
? ''
|
||||||
|
: `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const showFiat = Boolean(currentTokenInFiat) && currentCurrency.toUpperCase() !== symbol
|
const showFiat = Boolean(currentTokenInFiat) && currentCurrency.toUpperCase() !== symbol
|
||||||
@ -104,7 +106,7 @@ TokenCell.prototype.render = function () {
|
|||||||
|
|
||||||
h(Identicon, {
|
h(Identicon, {
|
||||||
className: 'token-list-item__identicon',
|
className: 'token-list-item__identicon',
|
||||||
diameter: 45,
|
diameter: 50,
|
||||||
address,
|
address,
|
||||||
network,
|
network,
|
||||||
}),
|
}),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const Component = require('react').Component
|
const Component = require('react').Component
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
|
||||||
const EthBalance = require('./eth-balance')
|
const EthBalance = require('./eth-balance')
|
||||||
const addressSummary = require('../util').addressSummary
|
const addressSummary = require('../util').addressSummary
|
||||||
@ -9,18 +10,33 @@ const CopyButton = require('./copyButton')
|
|||||||
const vreme = new (require('vreme'))()
|
const vreme = new (require('vreme'))()
|
||||||
const Tooltip = require('./tooltip')
|
const Tooltip = require('./tooltip')
|
||||||
const numberToBN = require('number-to-bn')
|
const numberToBN = require('number-to-bn')
|
||||||
|
const actions = require('../actions')
|
||||||
|
|
||||||
const TransactionIcon = require('./transaction-list-item-icon')
|
const TransactionIcon = require('./transaction-list-item-icon')
|
||||||
const ShiftListItem = require('./shift-list-item')
|
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)
|
inherits(TransactionListItem, Component)
|
||||||
function TransactionListItem () {
|
function TransactionListItem () {
|
||||||
Component.call(this)
|
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 () {
|
TransactionListItem.prototype.render = function () {
|
||||||
const { transaction, network, conversionRate, currentCurrency } = this.props
|
const { transaction, network, conversionRate, currentCurrency } = this.props
|
||||||
|
const { status } = transaction
|
||||||
if (transaction.key === 'shapeshift') {
|
if (transaction.key === 'shapeshift') {
|
||||||
if (network === '1') return h(ShiftListItem, transaction)
|
if (network === '1') return h(ShiftListItem, transaction)
|
||||||
}
|
}
|
||||||
@ -32,7 +48,7 @@ TransactionListItem.prototype.render = function () {
|
|||||||
|
|
||||||
var isMsg = ('msgParams' in transaction)
|
var isMsg = ('msgParams' in transaction)
|
||||||
var isTx = ('txParams' in transaction)
|
var isTx = ('txParams' in transaction)
|
||||||
var isPending = transaction.status === 'unapproved'
|
var isPending = status === 'unapproved'
|
||||||
let txParams
|
let txParams
|
||||||
if (isTx) {
|
if (isTx) {
|
||||||
txParams = transaction.txParams
|
txParams = transaction.txParams
|
||||||
@ -44,7 +60,7 @@ TransactionListItem.prototype.render = function () {
|
|||||||
|
|
||||||
const isClickable = ('hash' in transaction && isLinkable) || isPending
|
const isClickable = ('hash' in transaction && isLinkable) || isPending
|
||||||
return (
|
return (
|
||||||
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
h('.transaction-list-item.flex-column', {
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
if (isPending) {
|
if (isPending) {
|
||||||
this.props.showTx(transaction.id)
|
this.props.showTx(transaction.id)
|
||||||
@ -56,51 +72,92 @@ TransactionListItem.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
padding: '20px 0',
|
padding: '20px 0',
|
||||||
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
|
h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
||||||
h('.identicon-wrapper.flex-column.flex-center.select-none', [
|
style: {
|
||||||
h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
|
width: '100%',
|
||||||
]),
|
},
|
||||||
|
|
||||||
h(Tooltip, {
|
|
||||||
title: 'Transaction Number',
|
|
||||||
position: 'right',
|
|
||||||
}, [
|
}, [
|
||||||
h('span', {
|
h('.identicon-wrapper.flex-column.flex-center.select-none', [
|
||||||
|
h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h(Tooltip, {
|
||||||
|
title: 'Transaction Number',
|
||||||
|
position: 'right',
|
||||||
|
}, [
|
||||||
|
h('span', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
cursor: 'normal',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '10px',
|
||||||
|
},
|
||||||
|
}, nonce),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
|
||||||
|
domainField(txParams),
|
||||||
|
h('div', date),
|
||||||
|
recipientField(txParams, transaction, isTx, isMsg),
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Places a copy button if tx is successful, else places a placeholder empty div.
|
||||||
|
transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
|
||||||
|
|
||||||
|
isTx ? h(EthBalance, {
|
||||||
|
value: txParams.value,
|
||||||
|
conversionRate,
|
||||||
|
currentCurrency,
|
||||||
|
width: '55px',
|
||||||
|
shorten: true,
|
||||||
|
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: {
|
style: {
|
||||||
display: 'flex',
|
paddingRight: '2px',
|
||||||
cursor: 'normal',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: '10px',
|
|
||||||
},
|
},
|
||||||
}, nonce),
|
}, 'Taking too long?'),
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
textDecoration: 'underline',
|
||||||
|
},
|
||||||
|
}, 'Retry with a higher gas price here'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
|
|
||||||
domainField(txParams),
|
|
||||||
h('div', date),
|
|
||||||
recipientField(txParams, transaction, isTx, isMsg),
|
|
||||||
]),
|
|
||||||
|
|
||||||
// Places a copy button if tx is successful, else places a placeholder empty div.
|
|
||||||
transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
|
|
||||||
|
|
||||||
isTx ? h(EthBalance, {
|
|
||||||
value: txParams.value,
|
|
||||||
conversionRate,
|
|
||||||
currentCurrency,
|
|
||||||
width: '55px',
|
|
||||||
shorten: true,
|
|
||||||
showFiat: false,
|
|
||||||
style: {fontSize: '15px'},
|
|
||||||
}) : h('.flex-column'),
|
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionListItem.prototype.resubmit = function () {
|
||||||
|
const { transaction } = this.props
|
||||||
|
this.props.retryTransaction(transaction.id)
|
||||||
|
}
|
||||||
|
|
||||||
function domainField (txParams) {
|
function domainField (txParams) {
|
||||||
return h('div', {
|
return h('div', {
|
||||||
style: {
|
style: {
|
||||||
|
@ -170,6 +170,7 @@ TxListItem.prototype.getSendTokenTotal = async function () {
|
|||||||
TxListItem.prototype.render = function () {
|
TxListItem.prototype.render = function () {
|
||||||
const {
|
const {
|
||||||
transactionStatus,
|
transactionStatus,
|
||||||
|
transactionAmount,
|
||||||
onClick,
|
onClick,
|
||||||
transActionId,
|
transActionId,
|
||||||
dateString,
|
dateString,
|
||||||
@ -177,6 +178,7 @@ TxListItem.prototype.render = function () {
|
|||||||
className,
|
className,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { total, fiatTotal } = this.state
|
const { total, fiatTotal } = this.state
|
||||||
|
const showFiatTotal = transactionAmount !== '0x0' && fiatTotal
|
||||||
|
|
||||||
return h(`div${className || ''}`, {
|
return h(`div${className || ''}`, {
|
||||||
key: transActionId,
|
key: transActionId,
|
||||||
@ -232,13 +234,9 @@ TxListItem.prototype.render = function () {
|
|||||||
style: {},
|
style: {},
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
h('span', {
|
h('span.tx-list-value', total),
|
||||||
className: classnames('tx-list-value', {
|
|
||||||
'tx-list-value--confirmed': transactionStatus === 'confirmed',
|
|
||||||
}),
|
|
||||||
}, 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 () {
|
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-wrapper', [
|
||||||
h('div.flex-row.tx-list-header', [
|
h('div.flex-row.tx-list-header', [
|
||||||
h('div', 'transactions'),
|
h('div', 'transactions'),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
h('div.flex-column.tx-list-container', {}, [
|
||||||
this.renderTransaction(),
|
this.renderTransaction(),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
TxList.prototype.renderTransaction = function () {
|
TxList.prototype.renderTransaction = function () {
|
||||||
const { txsToRender, conversionRate } = this.props
|
const { txsToRender, conversionRate } = this.props
|
||||||
return txsToRender.length
|
return txsToRender.length
|
||||||
? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate))
|
? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate, i))
|
||||||
: [h(
|
: [h(
|
||||||
'div.tx-list-item.tx-list-item--empty',
|
'div.tx-list-item.tx-list-item--empty',
|
||||||
{ key: 'tx-list-none' },
|
{ key: 'tx-list-none' },
|
||||||
@ -67,12 +66,16 @@ TxList.prototype.renderTransaction = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Consider moving TxListItem into a separate component
|
// TODO: Consider moving TxListItem into a separate component
|
||||||
TxList.prototype.renderTransactionListItem = function (transaction, conversionRate) {
|
TxList.prototype.renderTransactionListItem = function (transaction, conversionRate, index) {
|
||||||
// console.log({transaction})
|
// console.log({transaction})
|
||||||
// refer to transaction-list.js:line 58
|
// refer to transaction-list.js:line 58
|
||||||
|
|
||||||
if (transaction.key === 'shapeshift') {
|
if (transaction.key === 'shapeshift') {
|
||||||
return h(ShiftListItem, transaction)
|
return h('div', {
|
||||||
|
key: `shapeshift${index}`,
|
||||||
|
}, [
|
||||||
|
h(ShiftListItem, transaction),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
|
@ -74,18 +74,14 @@ TxView.prototype.renderButtons = function () {
|
|||||||
return !selectedToken
|
return !selectedToken
|
||||||
? (
|
? (
|
||||||
h('div.flex-row.flex-center.hero-balance-buttons', [
|
h('div.flex-row.flex-center.hero-balance-buttons', [
|
||||||
h('button.btn-clear', {
|
h('button.btn-clear.hero-balance-button', {
|
||||||
style: {
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
onClick: () => showModal({
|
onClick: () => showModal({
|
||||||
name: 'BUY',
|
name: 'DEPOSIT_ETHER',
|
||||||
}),
|
}),
|
||||||
}, 'DEPOSIT'),
|
}, 'DEPOSIT'),
|
||||||
|
|
||||||
h('button.btn-clear', {
|
h('button.btn-clear.hero-balance-button', {
|
||||||
style: {
|
style: {
|
||||||
textAlign: 'center',
|
|
||||||
marginLeft: '0.8em',
|
marginLeft: '0.8em',
|
||||||
},
|
},
|
||||||
onClick: () => history.push(SEND_ROUTE),
|
onClick: () => history.push(SEND_ROUTE),
|
||||||
@ -94,11 +90,7 @@ TxView.prototype.renderButtons = function () {
|
|||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
h('div.flex-row.flex-center.hero-balance-buttons', [
|
h('div.flex-row.flex-center.hero-balance-buttons', [
|
||||||
h('button.btn-clear', {
|
h('button.btn-clear.hero-balance-button', {
|
||||||
style: {
|
|
||||||
textAlign: 'center',
|
|
||||||
marginLeft: '0.8em',
|
|
||||||
},
|
|
||||||
onClick: () => history.push(SEND_ROUTE),
|
onClick: () => history.push(SEND_ROUTE),
|
||||||
}, 'SEND'),
|
}, 'SEND'),
|
||||||
])
|
])
|
||||||
@ -114,7 +106,7 @@ TxView.prototype.render = function () {
|
|||||||
|
|
||||||
h('div.flex-row.phone-visible', {
|
h('div.flex-row.phone-visible', {
|
||||||
style: {
|
style: {
|
||||||
margin: '1em 0.9em',
|
margin: '1.5em 1.2em 0',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
@ -150,7 +142,7 @@ TxView.prototype.render = function () {
|
|||||||
|
|
||||||
!isMascara && h('div.open-in-browser', {
|
!isMascara && h('div.open-in-browser', {
|
||||||
onClick: () => global.platform.openExtensionInBrowser(),
|
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,
|
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(TokenList),
|
||||||
|
|
||||||
h('button.wallet-view__add-token-button', {
|
h('button.btn-clear.wallet-view__add-token-button', {
|
||||||
onClick: () => history.push(ADD_TOKEN_ROUTE),
|
onClick: () => history.push(ADD_TOKEN_ROUTE),
|
||||||
}, 'Add Token'),
|
}, 'Add Token'),
|
||||||
])
|
])
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(0, 0, 0, .05);
|
background-color: rgba(0, 0, 0, .05);
|
||||||
@ -164,9 +165,18 @@
|
|||||||
|
|
||||||
&__buttons {
|
&__buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: row nowrap;
|
||||||
margin: 30px 0 51px;
|
margin: 30px 0 51px;
|
||||||
flex: 0 0 auto;
|
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 {
|
&__token-icons-container {
|
||||||
@ -324,18 +334,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__buttons {
|
&__buttons {
|
||||||
flex-flow: row nowrap;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-top: 1px solid $gallery;
|
border-top: 1px solid $gallery;
|
||||||
|
width: 100%;
|
||||||
button {
|
|
||||||
flex: 1 0 auto;
|
|
||||||
margin: 0 12px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,43 @@
|
|||||||
background-color: #02c9b1; // TODO: reusable color in colors.css
|
background-color: #02c9b1; // TODO: reusable color in colors.css
|
||||||
}
|
}
|
||||||
|
|
||||||
button.btn-clear {
|
.btn-clear {
|
||||||
background: $white;
|
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
|
// No longer used in flat design, remove when modal buttons done
|
||||||
|
@ -2,13 +2,15 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
flex: 0 0 auto;
|
flex: 1 0 auto;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
box-shadow: 0 2px 4px 0 rgba($black, .08);
|
box-shadow: 0 2px 4px 0 rgba($black, .08);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
@media screen and (max-width: 575px) {
|
@media screen and (max-width: 575px) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
box-shadow: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 576px) {
|
@media screen and (min-width: 576px) {
|
||||||
@ -102,15 +104,10 @@
|
|||||||
|
|
||||||
.confirm-screen-back-button {
|
.confirm-screen-back-button {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid $curious-blue;
|
|
||||||
left: 24px;
|
left: 24px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: center;
|
padding: 6px 12px;
|
||||||
color: $curious-blue;
|
font-size: .7rem;
|
||||||
padding: 6px 13px 7px 12px;
|
|
||||||
border-radius: 2px;
|
|
||||||
height: 30px;
|
|
||||||
width: 54px;
|
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
@media screen and (max-width: $break-small) {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
@ -277,8 +274,8 @@ section .confirm-screen-account-number,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.confirm-screen-confirm-button {
|
.confirm-screen-confirm-button {
|
||||||
height: 62px;
|
height: 50px;
|
||||||
border-radius: 2px;
|
border-radius: 4px;
|
||||||
background-color: #02c9b1;
|
background-color: #02c9b1;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: $white;
|
color: $white;
|
||||||
@ -290,11 +287,11 @@ section .confirm-screen-account-number,
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
margin: 0 8px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-light.confirm-screen-cancel-button {
|
.btn-light.confirm-screen-cancel-button {
|
||||||
height: 62px;
|
height: 50px;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -303,12 +300,11 @@ section .confirm-screen-account-number,
|
|||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 32px;
|
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
margin: 0 8px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pending-tx-form {
|
#pending-tx-form {
|
||||||
@ -317,7 +313,7 @@ section .confirm-screen-account-number,
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row nowrap;
|
flex-flow: row nowrap;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
padding: 12px 18px;
|
padding: 12px;
|
||||||
border-bottom-left-radius: 8px;
|
border-bottom-left-radius: 8px;
|
||||||
border-bottom-right-radius: 8px;
|
border-bottom-right-radius: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -17,7 +17,16 @@
|
|||||||
@media screen and (min-width: 576px) {
|
@media screen and (min-width: 576px) {
|
||||||
height: 75px;
|
height: 75px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metafox-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header--initialized {
|
||||||
|
|
||||||
|
@media screen and (min-width: 576px) {
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -27,10 +36,6 @@
|
|||||||
bottom: -32px;
|
bottom: -32px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.metafox-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-header-contents {
|
.app-header-contents {
|
||||||
@ -53,7 +58,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1281px) {
|
@media screen and (min-width: 1281px) {
|
||||||
width: 65vw;
|
width: 62vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,8 +66,10 @@
|
|||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #22232c; // $shark
|
font-size: 1.1rem;
|
||||||
line-height: 29px;
|
position: relative;
|
||||||
|
padding-left: 15px;
|
||||||
|
color: #5b5d67;
|
||||||
|
|
||||||
@media screen and (max-width: 575px) {
|
@media screen and (max-width: 575px) {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 2.8em 2.37em .8em;
|
margin: 2.3em 2.37em .8em;
|
||||||
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.balance-container {
|
.balance-container {
|
||||||
@ -37,13 +38,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.balance-display {
|
.balance-display {
|
||||||
|
.token-amount {
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
@media screen and (max-width: $break-small) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.token-amount {
|
.token-amount {
|
||||||
font-size: 175%;
|
font-size: 1.75rem;
|
||||||
margin-top: 12.5%;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fiat-amount {
|
.fiat-amount {
|
||||||
@ -54,12 +58,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
@media screen and (min-width: $break-large) {
|
||||||
margin-left: 3%;
|
margin-left: .8em;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
.token-amount {
|
.token-amount {
|
||||||
font-size: 135%;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fiat-amount {
|
.fiat-amount {
|
||||||
@ -69,13 +73,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.balance-icon {
|
|
||||||
border-radius: 25px;
|
|
||||||
width: 45px;
|
|
||||||
height: 45px;
|
|
||||||
border: 1px solid $alto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-balance-buttons {
|
.hero-balance-buttons {
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
@media screen and (max-width: $break-small) {
|
||||||
@ -89,26 +86,9 @@
|
|||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
justify-content: flex-end;
|
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 './editable-label.scss';
|
||||||
|
|
||||||
@import './pages/index.scss';
|
@import './pages/index.scss';
|
||||||
|
|
||||||
|
@import './new-account.scss';
|
||||||
|
@ -258,19 +258,10 @@
|
|||||||
width: 286px;
|
width: 286px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-clear {
|
.account-modal__button {
|
||||||
min-height: 28px;
|
|
||||||
font-size: 14px;
|
|
||||||
border-color: $curious-blue;
|
|
||||||
color: $curious-blue;
|
|
||||||
border-radius: 2px;
|
|
||||||
flex-basis: 100%;
|
|
||||||
width: 75%;
|
|
||||||
margin-top: 17px;
|
margin-top: 17px;
|
||||||
padding: 10px 22px;
|
padding: 10px 22px;
|
||||||
height: 44px;
|
|
||||||
width: 235px;
|
width: 235px;
|
||||||
font-family: Roboto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,17 +337,17 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-clear {
|
.export-private-key__button {
|
||||||
width: 141px;
|
margin-top: 17px;
|
||||||
height: 54px;
|
padding: 10px 22px;
|
||||||
}
|
width: 141px;
|
||||||
|
height: 54px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-cancel {
|
.export-private-key__button--cancel {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
border-color: $dusty-gray;
|
|
||||||
color: $scorpion;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.private-key-password-display-wrapper {
|
.private-key-password-display-wrapper {
|
||||||
@ -495,10 +486,9 @@
|
|||||||
|
|
||||||
.hide-token-confirmation {
|
.hide-token-confirmation {
|
||||||
min-height: 250.72px;
|
min-height: 250.72px;
|
||||||
width: 374.49px;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: #FFFFFF;
|
background-color: $white;
|
||||||
box-shadow: 0 1px 7px 0 rgba(0,0,0,0.5);
|
box-shadow: 0 1px 7px 0 rgba(0, 0, 0, .5);
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
padding: 24px 27px 21px;
|
padding: 24px 27px 21px;
|
||||||
@ -508,7 +498,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__identicon {
|
&__identicon {
|
||||||
margin-bottom: 10px
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__symbol {
|
&__symbol {
|
||||||
@ -547,20 +537,11 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
&__button {
|
||||||
height: 44px;
|
width: 141px;
|
||||||
width: 113px;
|
margin: 0 5px;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -599,3 +580,256 @@
|
|||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
color: $nile-blue;
|
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,41 +8,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.network-component.pointer {
|
.network-component.pointer {
|
||||||
border: 1px solid $shark;
|
border: 2px solid $silver;
|
||||||
border-radius: 82px;
|
border-radius: 82px;
|
||||||
padding: 6px;
|
padding: 3px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
&.ethereum-network {
|
&.ethereum-network .menu-icon-circle div {
|
||||||
border-color: rgb(3, 135, 137);
|
background-color: rgba(3, 135, 137, .7) !important;
|
||||||
|
|
||||||
.menu-icon-circle div {
|
|
||||||
background-color: rgba(3, 135, 137, .7) !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ropsten-test-network {
|
&.ropsten-test-network .menu-icon-circle div {
|
||||||
border-color: rgb(233, 21, 80);
|
background-color: rgba(233, 21, 80, .7) !important;
|
||||||
|
|
||||||
.menu-icon-circle div {
|
|
||||||
background-color: rgba(233, 21, 80, .7) !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.kovan-test-network {
|
&.kovan-test-network .menu-icon-circle div {
|
||||||
border-color: rgb(105, 4, 150);
|
background-color: rgba(105, 4, 150, .7) !important;
|
||||||
|
|
||||||
.menu-icon-circle div {
|
|
||||||
background-color: rgba(105, 4, 150, .7) !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.rinkeby-test-network {
|
&.rinkeby-test-network .menu-icon-circle div {
|
||||||
border-color: rgb(235, 179, 63);
|
background-color: rgba(235, 179, 63, .7) !important;
|
||||||
|
|
||||||
.menu-icon-circle div {
|
|
||||||
background-color: rgba(235, 179, 63, .7) !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,11 +50,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.network-name {
|
.network-name {
|
||||||
line-height: 15px;
|
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
|
color: $tundora;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.network-droppo {
|
.network-droppo {
|
||||||
@ -167,3 +152,6 @@
|
|||||||
line-height: 18px;
|
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
|
// Component Colors
|
||||||
$tx-view-bg: $white;
|
$tx-view-bg: $white;
|
||||||
$wallet-view-bg: $wild-sand;
|
$wallet-view-bg: $alabaster;
|
||||||
|
|
||||||
// Main container
|
// Main container
|
||||||
.main-container {
|
.main-container {
|
||||||
@ -40,6 +40,8 @@ $wallet-view-bg: $wild-sand;
|
|||||||
|
|
||||||
.open-in-browser {
|
.open-in-browser {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
// wallet view and sidebar
|
// wallet view and sidebar
|
||||||
@ -47,7 +49,7 @@ $wallet-view-bg: $wild-sand;
|
|||||||
.wallet-view {
|
.wallet-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 33.5 1 33.5%;
|
flex: 32 1 32%;
|
||||||
width: 0;
|
width: 0;
|
||||||
background: $wallet-view-bg;
|
background: $wallet-view-bg;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
@ -69,22 +71,18 @@ $wallet-view-bg: $wild-sand;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__keyring-label {
|
&__keyring-label {
|
||||||
height: 40px;
|
height: 50px;
|
||||||
color: $dusty-gray;
|
color: $dusty-gray;
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
line-height: 40px;
|
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding: 0 20px;
|
padding: 17px 20px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__details-button {
|
&__details-button {
|
||||||
color: $curious-blue;
|
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
line-height: 13px;
|
border-radius: 17px;
|
||||||
text-align: center;
|
|
||||||
border: 1px solid $curious-blue;
|
|
||||||
border-radius: 10.5px;
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
@ -121,16 +119,14 @@ $wallet-view-bg: $wild-sand;
|
|||||||
|
|
||||||
&__add-token-button {
|
&__add-token-button {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
color: $dusty-gray;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 19px;
|
|
||||||
text-align: center;
|
|
||||||
margin: 36px auto;
|
margin: 36px auto;
|
||||||
border: 1px solid $dusty-gray;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-weight: 300;
|
|
||||||
background: none;
|
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);
|
background: rgb(250, 250, 250);
|
||||||
z-index: $sidebar-z-index;
|
z-index: $sidebar-z-index;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 56px;
|
top: 66px;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -199,7 +195,7 @@ $wallet-view-bg: $wild-sand;
|
|||||||
|
|
||||||
.main-container {
|
.main-container {
|
||||||
// margin-top: 6.9vh;
|
// margin-top: 6.9vh;
|
||||||
width: 85%;
|
width: 85vw;
|
||||||
height: 90vh;
|
height: 90vh;
|
||||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
|
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) {
|
@media screen and (min-width: 769px) {
|
||||||
.main-container {
|
.main-container {
|
||||||
// margin-top: 6.9vh;
|
// margin-top: 6.9vh;
|
||||||
width: 80%;
|
width: 80vw;
|
||||||
height: 82vh;
|
height: 82vh;
|
||||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
|
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) {
|
@media screen and (min-width: 1281px) {
|
||||||
.main-container {
|
.main-container {
|
||||||
// margin-top: 6.9vh;
|
// margin-top: 6.9vh;
|
||||||
width: 65%;
|
width: 62vw;
|
||||||
height: 82vh;
|
height: 82vh;
|
||||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
|
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
|
||||||
}
|
}
|
||||||
@ -239,14 +235,6 @@ $wallet-view-bg: $wild-sand;
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.btn-clear {
|
|
||||||
width: 93px;
|
|
||||||
height: 50px;
|
|
||||||
font-size: .7em;
|
|
||||||
background: $white;
|
|
||||||
border: 1px solid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// wallet view
|
// wallet view
|
||||||
@ -254,9 +242,9 @@ $wallet-view-bg: $wild-sand;
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
color: $scorpion;
|
color: $black;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: .9rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -526,8 +526,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__form {
|
&__form {
|
||||||
margin: 13px 0;
|
padding: 13px 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
@media screen and (max-width: $break-small) {
|
||||||
padding: 13px 0;
|
padding: 13px 0;
|
||||||
@ -651,11 +652,11 @@
|
|||||||
border: 1px solid $curious-blue;
|
border: 1px solid $curious-blue;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
padding: 5px;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
top: 14px;
|
top: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__sliders-icon {
|
&__sliders-icon {
|
||||||
@ -677,38 +678,13 @@
|
|||||||
border-top: 1px solid $alto;
|
border-top: 1px solid $alto;
|
||||||
background: $white;
|
background: $white;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__next-btn,
|
&__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 {
|
&__cancel-btn {
|
||||||
color: $dusty-gray;
|
width: 163px;
|
||||||
border-color: $dusty-gray;
|
margin: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__customize-gas {
|
&__customize-gas {
|
||||||
|
@ -12,7 +12,7 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&__token-balance {
|
&__token-balance {
|
||||||
font-size: 130%;
|
font-size: 1.5rem;
|
||||||
|
|
||||||
@media #{$wallet-balance-breakpoint-range} {
|
@media #{$wallet-balance-breakpoint-range} {
|
||||||
font-size: 105%;
|
font-size: 105%;
|
||||||
@ -34,7 +34,8 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
|||||||
}
|
}
|
||||||
|
|
||||||
&--active {
|
&--active {
|
||||||
background-color: rgba($wallet-balance-bg, 1);
|
background-color: $manatee;
|
||||||
|
color: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__identicon {
|
&__identicon {
|
||||||
@ -62,11 +63,11 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
|||||||
height: 55px;
|
height: 55px;
|
||||||
width: 191px;
|
width: 191px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: rgba(0,0,0,0.82);
|
background-color: rgba(0, 0, 0, .82);
|
||||||
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.5);
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .5);
|
||||||
position: fixed;
|
position: absolute;
|
||||||
margin-top: 20px;
|
top: 60px;
|
||||||
margin-left: 105px;
|
right: 25px;
|
||||||
z-index: 2000;
|
z-index: 2000;
|
||||||
|
|
||||||
&__close-area {
|
&__close-area {
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tx-list-header-wrapper {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
.tx-list-header {
|
.tx-list-header {
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
@ -32,13 +36,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
@media screen and (min-width: $break-large) {
|
||||||
.tx-list-header-wrapper {
|
|
||||||
flex: 0 0 55px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tx-list-header {
|
.tx-list-header {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: 1.5em 2.37em;
|
margin: 1.1em 2.37em .8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tx-list-container::-webkit-scrollbar {
|
.tx-list-container::-webkit-scrollbar {
|
||||||
@ -73,7 +73,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
@media screen and (min-width: $break-large) {
|
||||||
padding-bottom: 12px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,21 +91,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tx-list-date-wrapper {
|
.tx-list-date-wrapper {
|
||||||
|
margin-top: 6px;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
|
||||||
margin-top: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tx-list-content-wrapper {
|
.tx-list-content-wrapper {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
margin-top: 2px;
|
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -115,7 +107,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
.tx-list-status {
|
.tx-list-status {
|
||||||
font-size: 14px !important;
|
font-size: 12px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tx-list-account {
|
.tx-list-account {
|
||||||
@ -129,7 +121,7 @@
|
|||||||
|
|
||||||
.tx-list-fiat-value {
|
.tx-list-fiat-value {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 16px;
|
line-height: 22px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,7 +202,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
@media screen and (min-width: $break-large) {
|
||||||
margin: 0 2.37em;
|
padding: 0 2.37em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
@ -259,6 +251,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tx-list-fiat-value {
|
.tx-list-fiat-value {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: initial;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -8,7 +8,8 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
|||||||
background: rgba($wallet-balance-bg, 0);
|
background: rgba($wallet-balance-bg, 0);
|
||||||
|
|
||||||
&--active {
|
&--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;
|
align-items: flex-start;
|
||||||
|
|
||||||
.token-amount {
|
.token-amount {
|
||||||
font-size: 135%;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fiat-amount {
|
.fiat-amount {
|
||||||
@ -61,11 +62,13 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.balance-icon {
|
|
||||||
border-radius: 25px;
|
.balance-icon {
|
||||||
width: 45px;
|
border-radius: 25px;
|
||||||
height: 45px;
|
width: 50px;
|
||||||
border: 1px solid $alto;
|
height: 50px;
|
||||||
}
|
border: 1px solid $alto;
|
||||||
|
padding: 5px;
|
||||||
|
background: $white;
|
||||||
}
|
}
|
||||||
|
@ -51,14 +51,14 @@
|
|||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'DIN NEXT';
|
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-weight: 400;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'DIN NEXT Light';
|
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-weight: 400;
|
||||||
font-style: normal;
|
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