pull/2/merge
Szymon Lesisz 6 years ago
parent 6a9fde9f86
commit cb83b2b7b6

@ -0,0 +1,20 @@
[ignore]
.*/node_modules/bitcoinjs-lib-zcash/.*
.*/node_modules/bitcoinjs-lib/.*
.*/node_modules/hd-wallet/.*
.*/node_modules/protobufjs-old-fixed-webpack/src/bower.json
.*/node_modules/trezor-link/lib/.*
.*/_old/.*
[libs]
[options]
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
esproposal.export_star_as=enable
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
esproposal.decorators=ignore
module.name_mapper='.*\(.less\)' -> 'CSSModule'
module.system=haste

4
.gitignore vendored

@ -18,4 +18,6 @@ logs
.yarnclean
# Local config file
webpack/constants.js
webpack/constants.js
_old

@ -0,0 +1,13 @@
printf "\n-- DEPLOY START -----------------------\n"
yarn run build
printf "\n-- COPYING FILES ----------------------\n"
cd build
rsync -avz --delete -e ssh . admin@dev.sldev.cz:~/experiments/www
cd ../
printf "\n-- COMPLETE ---------------------------\n"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

@ -21,16 +21,26 @@
},
"dependencies": {
"babel-preset-react": "^6.24.1",
"color-hash": "^1.0.3",
"ethereumjs-tx": "^1.3.3",
"ethereumjs-units": "^0.2.0",
"ethereumjs-util": "^5.1.2",
"hdkey": "0.7.1",
"path-to-regexp": "^2.1.0",
"raf": "^3.4.0",
"rc-tooltip": "^3.7.0",
"react": "^16.1.1",
"react-blockies": "^1.2.2",
"react-css-transition": "^0.7.4",
"react-dom": "^16.1.1",
"react-ellipsis-text": "^1.0.0",
"react-hot-loader": "^3.1.3",
"react-qr-svg": "^2.1.0",
"react-redux": "^5.0.6",
"react-router-dom": "^4.2.2",
"react-router-redux": "next",
"react-scale-text": "^1.2.2",
"react-select": "^1.1.0",
"react-transition-group": "^2.2.1",
"redux": "^3.7.2",
"redux-logger": "^3.0.6",

Binary file not shown.

@ -0,0 +1,81 @@
{
"coins1": [
{
"name": "Ethereum Ropsten",
"symbol": "eth",
"network": "ropsten-eth",
"shortcut": "eth",
"bip44": "m/44'/60'/0'/0",
"defaultGasPrice": 64,
"defaultGasLimit": 21000,
"defaultGasLimitTokens": 200000,
"backends": [
{
"name": "TREZOR Wallet - Ethereum",
"urls": [
"https://ropsten.infura.io/QGyVKozSUEh2YhL4s2G4",
"http://10.34.2.5:8545"
],
"explorer": "https://blockexplorer.com"
}
]
}
],
"coins": [
{
"name": "Ethereum Ropsten",
"symbol": "eth",
"network": "ropsten-eth",
"shortcut": "eth",
"bip44": "m/44'/60'/0'/0",
"defaultGasPrice": 64,
"defaultGasLimit": 21000,
"defaultGasLimitTokens": 200000,
"backends": [
{
"name": "TREZOR Wallet - Ethereum",
"urls": [
"https://ropsten.infura.io/QGyVKozSUEh2YhL4s2G4",
"http://10.34.2.5:8545"
],
"explorer": "https://blockexplorer.com"
}
]
},
{
"name": "Ethereum Rinkeby",
"symbol": "etc",
"network": "ropsten-eth",
"shortcut": "etc",
"bip44": "m/44'/61'/0'/0",
"defaultGasPrice": 64,
"defaultGasLimit": 21000,
"defaultGasLimitTokens": 200000,
"backends": [
{
"name": "TREZOR Wallet - Ethereum",
"urls": [
"https://rinkeby.infura.io/QGyVKozSUEh2YhL4s2G4",
"http://10.34.2.5:8545"
],
"explorer": "https://blockexplorer.com"
}
]
}
],
"fiatValueTickers": [
],
"bridge": {
"url": "https://localback.net:21324",
"configUrl": "data/config_signed.bin",
"latestUrl": "data/bridge/latest.txt"
},
"extensionId": "jcjjhjgimijdkoamemaghajlhegmoclj",
"storageVersion": "1.1.0",
"metadataVersion": "1.0.0"
}

@ -0,0 +1,152 @@
[
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name":"",
"type":"string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{"name":"_spender", "type":"address"},
{"name":"_value","type":"uint256"}
],
"name":"approve",
"outputs":[
{"name":"success","type":"bool"}
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":true,
"inputs":[],
"name":"totalSupply",
"outputs":[
{"name":"","type":"uint256"}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":false,
"inputs":[
{"name":"_from","type":"address"},
{"name":"_to","type":"address"},
{"name":"_value","type":"uint256"}
],
"name":"transferFrom",
"outputs":[
{"name":"success","type":"bool"}
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":true,
"inputs":[],
"name":"decimals",
"outputs":[
{"name":"","type":"uint8"}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[],
"name":"version",
"outputs":[
{"name":"","type":"string"}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},
{
"constant":false,
"inputs":[
{"name":"_to","type":"address"},
{"name":"_value","type":"uint256"}
],
"name":"transfer",
"outputs":[
{"name":"success","type":"bool"}
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{"name":"_spender","type":"address"},
{"name":"_value","type":"uint256"},
{"name":"_extraData","type":"bytes"}
],
"name":"approveAndCall",
"outputs":[
{"name":"success","type":"bool"}
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":true,
"inputs":[
{"name":"_owner","type":"address"},
{"name":"_spender","type":"address"}
],
"name":"allowance",
"outputs":[
{"name":"remaining","type":"uint256"}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"inputs":[],
"payable":false,
"stateMutability":"nonpayable",
"type":"constructor"
},
{
"payable":false,
"stateMutability":"nonpayable",
"type":"fallback"
},{
"anonymous":false,
"inputs":[
{"indexed":true,"name":"_from","type":"address"},
{"indexed":true,"name":"_to","type":"address"},
{"indexed":false,"name":"_value","type":"uint256"}
],
"name":"Transfer",
"type":"event"
},{
"anonymous":false,
"inputs":[
{"indexed":true,"name":"_owner","type":"address"},
{"indexed":true,"name":"_spender","type":"address"},
{"indexed":false,"name":"_value","type":"uint256"}
],
"name":"Approval",
"type":"event"
}
]

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -0,0 +1,21 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="819.2" descent="-204.8" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" />
<glyph unicode="&#x2b;" glyph-name="plus" d="M384 951.467h170.667q17.92 0 30.293-12.373t12.373-30.293v-298.667h298.667q17.92 0 30.293-12.373t12.373-30.293v-170.667q0-17.92-12.373-30.293t-30.293-12.373h-298.667v-298.667q0-17.92-12.373-30.293t-30.293-12.373h-170.667q-17.92 0-30.293 12.373t-12.373 30.293v298.667h-298.667q-17.92 0-30.293 12.373t-12.373 30.293v170.667q0 17.92 12.373 30.293t30.293 12.373h298.667v298.667q0 17.92 12.373 30.293t30.293 12.373z" />
<glyph unicode="&#x5f;" glyph-name="trezor" horiz-adv-x="679" d="M339.471 896c-142.287 0-257.634-115.347-257.634-257.634v-96.329c-49.928-8.931-81.837-21.275-81.837-37.035v-513.942l335.777-119.061 343.165 119.061v513.942c0 15.76-31.909 28.104-81.837 37.035v96.329c0 142.287-115.347 257.634-257.634 257.634zM339.471 789.915c83.698 0 151.549-67.852 151.549-151.549v-83.92c-94.502 6.876-208.597 6.876-303.099 0v83.92c0 83.698 67.852 151.549 151.549 151.549zM333.030 430.554c117.072-0.19 233.765-8.903 233.765-25.574v-324.695l-231.208-85.531-238.596 85.531v324.695c1.515 17.428 118.966 25.763 236.038 25.574z" />
<glyph unicode="&#xe019;" glyph-name="settings" d="M512 1014.613q28.16 0 63.147-4.267l32.427-129.707 4.267-0.853q41.813-11.947 80.213-33.28l4.267-1.707 114.347 68.267q52.053-40.96 88.747-89.6l-68.267-114.347 2.56-4.267q21.333-37.547 33.28-79.36l0.853-5.12 129.707-32.427q4.267-36.693 4.267-62.293 0-29.013-4.267-63.147l-129.707-32.427-0.853-5.12q-12.8-41.813-33.28-79.36l-2.56-4.267 68.267-114.347q-40.96-52.053-88.747-89.6l-114.347 69.12-4.267-2.56q-37.547-21.333-80.213-33.28l-4.267-1.707-32.427-128.853q-36.693-4.267-63.147-4.267-28.16 0-63.147 4.267l-32.427 128.853-4.267 1.707q-41.813 11.947-80.213 33.28l-4.267 2.56-114.347-69.12q-51.2 40.96-88.747 89.6l68.267 114.347-2.56 4.267q-21.333 38.4-32.427 79.36l-1.707 5.12-128.853 32.427q-5.12 35.84-5.12 63.147 0 28.16 5.12 62.293l128.853 32.427 1.707 5.12q11.093 40.96 32.427 79.36l2.56 4.267-68.267 114.347q40.107 52.053 89.6 89.6l113.493-68.267 4.267 1.707q38.4 21.333 80.213 33.28l4.267 0.853 32.427 129.707q36.693 4.267 63.147 4.267zM512 708.267q-75.947 0-129.707-53.76t-53.76-129.28 53.76-129.28 129.707-53.76 129.707 53.76 53.76 129.28-53.76 129.28-129.707 53.76z" />
<glyph unicode="&#xe031;" glyph-name="refresh" d="M808.107 917.333l115.2 115.2q5.973 5.973 10.667 4.267t4.693-11.093v-308.053q0-9.387-6.4-15.787t-15.787-6.4h-308.053q-9.387 0-11.093 4.693t4.267 10.667l114.347 114.347q-93.867 64-203.947 64-98.987 0-183.040-48.64t-132.693-132.693-48.64-183.040h-128q0 99.84 38.827 191.147t104.96 157.44 157.44 104.96 191.147 38.827q163.84 0 296.107-99.84zM876.373 524.8h128q0-99.84-38.827-191.147t-104.96-157.44-157.44-104.96-191.147-38.827q-163.84 0-296.96 100.693l-114.347-114.347q-5.973-6.827-10.667-4.693t-4.693 10.667v307.2q0 9.387 6.4 15.787t15.787 6.4h307.2q8.533 0 10.667-4.693t-4.693-10.667l-113.493-113.493q93.867-64.853 204.8-64.853 98.987 0 183.040 48.64t132.693 132.693 48.64 183.040z" />
<glyph unicode="&#xe105;" glyph-name="eye" d="M600 966q82 0 160.5-22.5t140-59 116.5-82.5 94.5-95 68-95 42.5-82.5 14-57.5-14-57.5-43-82.5-68.5-95-94.5-95-116.5-82.5-140-59-159.5-22.5-159.5 22.5-140 59-116.5 82.5-94.5 95-68.5 95-43 82.5-14 57.5 14 57.5 42.5 82.5 68 95 94.5 95 116.5 82.5 140 59 160.5 22.5zM888 701q-15 15-18 12t5-22q25-57 25-119 0-124-88-212t-212-88-212 88-88 212q0 59 23 114 8 19 4.5 22t-17.5-12q-70-69-160-184-13-16-15-40.5t9-42.5q22-36 47-71t70-82 92.5-81 113-58.5 133.5-24.5 133.5 24 113 58.5 92.5 81.5 70 81.5 47 70.5q11 18 9 42.5t-14 41.5q-90 117-163 189zM448 599l-35-36q-15-15-19.5-38.5t4.5-41.5q37-68 93-116 16-13 38.5-11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44-89 117-11 18-28 20t-32-12z" />
<glyph unicode="&#xe113;" glyph-name="arrow-up" d="M938.667 363.52l-168.96-169.813-301.227 301.227-301.227-301.227-168.107 169.813 470.187 470.187z" />
<glyph unicode="&#xe114;" glyph-name="arrow-dn" d="M939.52 686.080l-469.333-470.187-470.187 470.187 168.96 169.813 301.227-301.227 301.227 301.227z" />
<glyph unicode="&#xea07;" glyph-name="warning" d="M512 739.226l429.102-855.226h-858.206l429.104 855.226zM512 832c-22.070 0-44.14-14.882-60.884-44.648l-437.074-871.112c-33.486-59.532-5-108.24 63.304-108.24h869.308c68.3 0 96.792 48.708 63.3 108.24h0.002l-437.074 871.112c-16.742 29.766-38.812 44.648-60.882 44.648v0zM576 0c0-35.346-28.654-64-64-64s-64 28.654-64 64c0 35.346 28.654 64 64 64s64-28.654 64-64zM512 128c-35.346 0-64 28.654-64 64v192c0 35.346 28.654 64 64 64s64-28.654 64-64v-192c0-35.346-28.654-64-64-64z" />
<glyph unicode="&#xea0c;" glyph-name="info" d="M448 528c0 26.4 21.6 48 48 48h32c26.4 0 48-21.6 48-48v-32c0-26.4-21.6-48-48-48h-32c-26.4 0-48 21.6-48 48v32zM640 64h-256v64h64v192h-64v64h192v-256h64zM512 832c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-96c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416z" />
<glyph unicode="&#xea0f;" glyph-name="cross" d="M1014.662 9.34c-0.004 0.004-0.008 0.008-0.012 0.010l-310.644 310.65 310.644 310.65c0.004 0.004 0.008 0.006 0.012 0.010 3.344 3.346 5.762 7.254 7.312 11.416 4.246 11.376 1.824 24.682-7.324 33.83l-146.746 146.746c-9.148 9.146-22.45 11.566-33.828 7.32-4.16-1.55-8.070-3.968-11.418-7.31 0-0.004-0.004-0.006-0.008-0.010l-310.648-310.652-310.648 310.65c-0.004 0.004-0.006 0.006-0.010 0.010-3.346 3.342-7.254 5.76-11.414 7.31-11.38 4.248-24.682 1.826-33.83-7.32l-146.748-146.748c-9.148-9.148-11.568-22.452-7.322-33.828 1.552-4.16 3.97-8.072 7.312-11.416 0.004-0.002 0.006-0.006 0.010-0.010l310.65-310.648-310.65-310.652c-0.002-0.004-0.006-0.006-0.008-0.010-3.342-3.346-5.76-7.254-7.314-11.414-4.248-11.376-1.826-24.682 7.322-33.83l146.748-146.746c9.15-9.148 22.452-11.568 33.83-7.322 4.16 1.552 8.070 3.97 11.416 7.312 0.002 0.004 0.006 0.006 0.010 0.010l310.648 310.65 310.648-310.65c0.004-0.002 0.008-0.006 0.012-0.008 3.348-3.344 7.254-5.762 11.414-7.314 11.378-4.246 24.684-1.826 33.828 7.322l146.746 146.748c9.148 9.148 11.57 22.454 7.324 33.83-1.552 4.16-3.97 8.068-7.314 11.414z" />
<glyph unicode="&#xea10;" glyph-name="checkmark" d="M864 704l-480-480-224 224-160-160 384-384 640 640z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,41 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="icon-unlocked" d="M381.088 483.648v89.696c0 72.16 53.696 130.656 119.904 130.656h35.104c66.208 0 119.904-58.496 119.904-130.656v-7.616h-69.312c-4.416 36.704-33.248 65.056-68.128 65.056-37.952 0-68.736-33.536-68.736-74.88v-72.288h221.184c18.208 0 32.992-15.552 32.992-34.72v-222.24c0-19.104-14.784-34.656-32.992-34.656h-318.016c-18.208 0-32.992 15.552-32.992 34.72v222.24c0 19.168 14.784 34.72 32.992 34.72h28.096z" />
<glyph unicode="&#xe901;" glyph-name="icon-locked" d="M671.008 483.648h-14.976v89.632c0 72.256-53.76 130.72-120.064 130.72h-35.136c-66.304 0-119.744-58.464-119.744-130.72v-89.632h-28.096c-18.336 0-32.992-15.424-32.992-34.688v-222.272c0-18.944 14.656-34.688 32.992-34.688h318.016c18.336 0 32.992 15.744 32.992 34.688v222.272c0 19.264-14.656 34.688-32.992 34.688zM449.824 555.936c0 41.44 30.56 74.848 68.736 74.848 34.816 0 63.552-33.728 68.128-70.336v-76.768h-136.864v72.256z" />
<glyph unicode="&#xe902;" glyph-name="icon-eject" d="M276 192h471.968c11.072 0 20.032 9.76 20.032 21.824v75.968c0 12.064-8.96 21.824-20 21.824h-472c-11.040 0-20-9.76-20-21.824v-75.968c0-12.064 8.96-21.824 20-21.824zM503.552 699.808l-231.232-288.128c-6.368-7.904-1.184-20.32 8.448-20.32h462.496c9.664 0 14.816 12.384 8.448 20.32l-231.232 288.128c-4.512 5.6-12.448 5.6-16.928 0z" />
<glyph unicode="&#xe903;" glyph-name="icon-refresh" d="M347.392 486.688c17.28 82.24 90.4 142.336 173.92 142.336 31.648 0 61.92-8.704 88.576-24.416 6.656-3.936 12.8-8.672 18.944-13.504l-68.832-68.736 192-33.056-32 198.272-38.4-42.016c-5.92 5.024-12.064 9.728-18.336 14.144-40.928 28.672-89.92 44.288-141.92 44.288-121.664 0-225.12-89.312-245.984-210.368l-3.36-20.896h72.672l2.72 13.952zM676.608 409.312c-17.28-82.24-90.4-142.336-173.92-142.336-31.648 0-61.92 8.704-88.576 24.416-6.624 3.936-12.8 8.672-18.944 13.504l68.832 68.736-192 33.056 32-198.272 38.4 42.016c5.92-5.024 12.032-9.696 18.336-14.144 40.928-28.672 89.92-44.288 141.952-44.288 121.664 0 225.12 89.312 245.984 210.368l3.328 20.864h-72.672l-2.72-13.92z" />
<glyph unicode="&#xe904;" glyph-name="icon-info" d="M693.024 629.056c-99.968 99.936-262.080 99.936-362.048 0s-99.968-262.112 0-362.080c99.968-100 262.144-99.936 362.048 0 99.968 99.904 99.968 262.176 0 362.080zM507.904 659.808c27.008 0 48.992-21.984 48.992-49.088 0-27.296-21.984-49.472-48.992-49.472-27.264 0-49.536 22.176-49.536 49.472 0 27.552 21.728 49.088 49.536 49.088zM586.656 299.2c0-10.304-4.96-15.328-15.264-15.328h-126.464c-10.304 0-15.328 5.024-15.328 15.328v32.256c0 10.304 5.024 15.264 15.328 15.264h23.36v136.064h-23.872c-10.304 0-15.264 5.024-15.264 15.328v32.224c0 10.304 4.96 15.264 15.264 15.264h88.288c10.304 0 15.264-4.96 15.264-15.264v-183.648h23.424c10.304 0 15.264-4.96 15.264-15.264v-32.224z" />
<glyph unicode="&#xe905;" glyph-name="icon-chat" d="M580.992 704h-137.984c-103.296 0-187.008-85.952-187.008-192 0-96.608 69.536-176.32 160-189.792v-130.208l128 128h36.992c103.296 0 187.008 85.952 187.008 192s-83.712 192-187.008 192z" />
<glyph unicode="&#xe906;" glyph-name="icon-skip" d="M512 704c-141.376 0-256-114.656-256-256 0-141.408 114.624-256 256-256s256 114.592 256 256c0 141.344-114.624 256-256 256zM529.056 328.544v68.256c-102.4 34.144-136.544 0-170.656-68.256 0 170.656 102.4 204.8 170.656 204.8v68.256l136.544-136.544-136.544-136.512z" />
<glyph unicode="&#xe907;" glyph-name="icon-cog" d="M739.552 497.856h-71.328c-4.256 13.664-10.208 26.56-17.472 38.56l47.264 47.424c11.2 11.008 11.2 29.056 0 40.192l-20.064 20.032c-11.136 11.104-29.152 11.040-40.192 0l-48.128-48.032c-12.992 7.392-27.072 13.152-42.080 16.992v62.496c0 15.68-12.672 28.48-28.448 28.48h-28.448c-15.68 0-28.416-12.8-28.416-28.48v-62.464c-16.352-4.128-31.68-10.656-45.728-19.2l-40.288 40.224c-11.072 11.040-29.184 11.104-40.288 0l-20.096-20.096c-11.104-11.072-10.976-29.152 0.064-40.288l40.992-40.992c-8.672-15.136-15.168-31.648-18.88-49.152h-53.504c-15.776 0-28.544-12.736-28.544-28.48v-28.416c0-15.68 12.768-28.416 28.544-28.416h57.152c5.184-17.152 12.992-32.928 23.008-47.328l-38.656-38.656c-11.136-11.136-11.136-29.216-0.064-40.288l20.064-20.096c11.2-11.040 29.248-11.040 40.32 0.032l43.232 43.2c14.528-7.232 30.336-12.48 46.944-15.2v-59.488c0-15.68 12.736-28.448 28.448-28.48h28.448c15.68 0.032 28.448 12.8 28.448 28.48v66.816c14.336 5.088 27.904 11.872 40.224 20.544l45.76-45.888c11.104-11.072 29.12-11.072 40.224 0l20.096 20.128c11.168 11.072 11.168 29.056-0.096 40.288l-50.144 50.24c6.144 12.512 10.944 25.792 13.92 39.904h67.776c15.744 0 28.448 12.672 28.48 28.448v28.448c-0.096 15.68-12.8 28.512-28.544 28.512zM504.928 376.928c-39.264 0-71.072 31.776-71.072 71.104 0 39.264 31.808 71.040 71.072 71.040 39.296 0 71.136-31.776 71.136-71.040 0-39.328-31.84-71.104-71.136-71.104z" />
<glyph unicode="&#xe908;" glyph-name="icon-warning" d="M795.616 224.992l-264.896 465.44c-10.272 18.080-27.168 18.080-37.504 0l-264.864-465.44c-10.272-18.176-1.696-32.992 19.040-32.992h529.184c20.8 0 29.376 14.816 19.040 32.992zM549.76 286.88c0-10.464-8.48-18.976-18.912-18.976h-37.792c-10.336 0-18.912 8.512-18.912 18.976v37.952c0 10.464 8.576 18.976 18.912 18.976h37.792c10.4 0 18.912-8.544 18.912-18.976v-37.952zM549.76 400.736c0-10.464-8.48-18.976-18.912-18.976h-37.792c-10.336 0-18.912 8.512-18.912 18.976v113.856c0 10.464 8.576 18.976 18.912 18.976h37.792c10.4 0 18.912-8.544 18.912-18.976v-113.856z" />
<glyph unicode="&#xe909;" glyph-name="icon-arrow-down" d="M757.216 539.072l-219.616-237.44c-8.128-8.576-19.296-13.632-31.040-13.632-11.744 0.288-23.2 5.056-31.040 13.664l-208.768 227.040c-15.36 16.928-14.176 43.040 3.008 58.176 16.864 15.424 43.392 13.952 59.040-2.656l177.76-193.504 188.608 203.904c7.52 8 18.080 12.768 29.216 13.344 11.456 0.608 21.696-3.264 30.112-10.688 16.896-15.456 18.080-41.568 2.72-58.208z" />
<glyph unicode="&#xe90a;" glyph-name="icon-close" d="M754.816 270.080c17.6-17.6 17.6-46.72 0-64.64-8.96-8.64-20.48-13.44-32.64-13.44s-23.68 4.8-32.32 13.44l-177.888 177.92-177.888-177.92c-16.32-16.96-47.040-17.6-64.64 0-17.92 17.92-17.92 47.040 0 64.64l178.208 177.92-178.208 177.92c-17.92 17.92-17.92 46.72 0 64.64 17.28 17.28 47.36 17.28 64.64 0l177.888-177.92 177.888 177.92c17.92 17.92 47.040 17.92 64.96 0 17.6-17.92 17.6-46.72 0-64.64l-178.24-177.92 178.24-177.92z" />
<glyph unicode="&#xe90b;" glyph-name="icon-arrow-left" d="M603.072 202.784l-237.44 219.616c-8.576 8.128-13.632 19.296-13.632 31.040 0.288 11.744 5.056 23.2 13.664 31.040l227.040 208.768c16.928 15.36 43.040 14.176 58.176-3.008 15.424-16.864 13.952-43.392-2.656-59.040l-193.504-177.76 203.904-188.608c8-7.52 12.768-18.080 13.344-29.216 0.608-11.456-3.264-21.696-10.688-30.112-15.456-16.896-41.568-18.080-58.208-2.72z" />
<glyph unicode="&#xe90c;" glyph-name="icon-arrow-up" d="M757.216 356.928l-219.616 237.44c-8.128 8.576-19.296 13.632-31.040 13.632-11.744-0.288-23.2-5.056-31.040-13.664l-208.768-227.040c-15.36-16.928-14.176-43.040 3.008-58.176 16.864-15.424 43.392-13.952 59.040 2.656l177.76 193.504 188.608-203.904c7.52-8 18.080-12.768 29.216-13.344 11.456-0.608 21.696 3.264 30.112 10.688 16.896 15.456 18.080 41.568 2.72 58.208z" />
<glyph unicode="&#xe90d;" glyph-name="icon-arrow-right2" d="M420.928 202.784l237.44 219.616c8.576 8.128 13.632 19.296 13.632 31.040-0.288 11.744-5.056 23.2-13.664 31.040l-227.040 208.768c-16.928 15.36-43.040 14.176-58.176-3.008-15.424-16.864-13.952-43.392 2.656-59.040l193.504-177.76-203.904-188.608c-8-7.52-12.768-18.080-13.344-29.216-0.608-11.456 3.264-21.696 10.688-30.112 15.456-16.896 41.568-18.080 58.208-2.72z" />
<glyph unicode="&#xe90e;" glyph-name="icon-plus" d="M768 448c0-22.080-17.92-40-40-40h-176v-176c0-22.080-17.92-40-40-40s-40 17.92-40 40v176h-176c-22.080 0-40 17.92-40 40s17.92 40 40 40h176v176c0 22.080 17.92 40 40 40s40-17.92 40-40v-176h176c22.080 0 40-17.92 40-40z" />
<glyph unicode="&#xe90f;" glyph-name="icon-help" d="M693.024 629.056c-99.968 99.936-262.080 99.936-362.048 0s-99.968-262.112 0-362.080c99.968-100 262.144-99.936 362.048 0 99.968 99.904 99.968 262.176 0 362.080zM501.216 242.048c-27.808 0-50.496 22.464-50.496 50.048 0 28.32 22.176 50.528 50.496 50.528 27.616 0 50.048-22.656 50.048-50.528 0.032-27.168-22.88-50.048-50.048-50.048zM536.416 417.536v-27.744c0-13.504-5.28-18.784-18.784-18.784h-36.224c-13.504 0-18.72 5.28-18.72 18.784v61.984c0 15.68 16.064 20.352 30.208 24.48 3.456 1.056 7.040 2.080 10.496 3.264 18.336 6.592 29.696 14.816 29.696 35.296 0 6.656 0 26.816-32.832 26.816-20.224 0-38.624-7.776-49.6-12.416-6.208-2.624-9.28-3.904-12.384-3.904-6.336 0-12.32 5.088-13.248 10.304l-12.608 32.96c-1.824 3.776-1.824 6.784-1.824 9.216 0 24.288 75.552 37.664 100.608 37.664 63.104 0 105.504-40.672 105.504-101.152 0.032-65.44-49.12-85.952-80.288-96.768z" />
<glyph unicode="&#xe910;" glyph-name="icon-dashboard" d="M768 672v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM480 384v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM480 672v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM768 384v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32z" />
<glyph unicode="&#xe911;" glyph-name="icon-arrow-right" d="M420.928 202.784l237.44 219.616c8.576 8.128 13.632 19.296 13.632 31.040-0.288 11.744-5.056 23.2-13.664 31.040l-227.040 208.768c-16.928 15.36-43.040 14.176-58.176-3.008-15.424-16.864-13.952-43.392 2.656-59.040l193.504-177.76-203.904-188.608c-8-7.52-12.768-18.080-13.344-29.216-0.608-11.456 3.264-21.696 10.688-30.112 15.456-16.896 41.568-18.080 58.208-2.72z" />
<glyph unicode="&#xe912;" glyph-name="icon-eye-crossed" d="M768 456.64c-20.16-34.88-44.48-63.68-71.68-86.72l-64.64 64.64c0.64 4.16 0.96 8.64 0.96 12.8 0 60.16-51.84 108.8-115.84 108.8-2.24 0-4.16 0-6.4-0.32l-33.92 33.92c12.16 1.6 24 2.24 36.16 2.24 98.88 0 197.44-45.12 255.36-135.36zM348.8 646.080c-8.96 8.96-23.68 8.96-32.64 0l-3.52-3.52c-8.96-8.96-8.96-23.68 0-32.64l53.76-53.76c-43.52-22.4-81.6-56.32-110.4-100.8 83.84-130.56 226.88-177.6 348.48-137.28l57.6-57.6c9.28-9.28 23.68-9.28 32.96 0l3.2 3.2c8.96 8.96 8.96 23.68 0 32.64l-349.44 349.76zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 21.12 6.080 40.64 17.28 57.28l42.56-42.56c-1.92-5.76-2.56-12.16-1.92-18.88 2.88-30.080 30.72-52.16 62.72-49.92 2.24 0.32 4.8 0.64 7.040 0.96l42.56-42.56c-16.32-8.32-34.56-12.8-54.4-12.8z" />
<glyph unicode="&#xe913;" glyph-name="icon-T1" d="M603.2 694.4h-6.4c-25.494 5.341-54.79 8.398-84.8 8.398s-59.305-3.058-87.592-8.879l2.792 0.48h-6.72c-30.053-5.643-52.489-31.68-52.489-62.956 0-0.367 0.003-0.733 0.009-1.099l-0.001 0.055v-234.88c0.075-40.921 11.238-79.22 30.643-112.071l-0.563 1.031 35.2-60.48c11.655-19.297 32.515-32.001 56.342-32.001 0.105 0 0.209 0 0.314 0.001h44.144c0.359-0.007 0.783-0.011 1.208-0.011 23.569 0 44.162 12.74 55.269 31.709l0.164 0.302 36.16 64c18.232 31.447 29.027 69.173 29.12 109.413v232.987c0.005 0.293 0.008 0.639 0.008 0.986 0 31.391-22.599 57.503-52.416 62.954l-0.392 0.059zM629.76 396.8c-0.193-35.364-9.792-68.446-26.418-96.923l0.498 0.923-35.84-64c-6.868-11.865-19.463-19.742-33.906-19.84h-44.174c-0.073 0-0.159-0.001-0.246-0.001-14.427 0-27.041 7.762-33.894 19.338l-0.1 0.183-34.88 59.84c-16.656 28.155-26.515 62.042-26.56 98.227v235.853c0.133 19.025 13.742 34.833 31.751 38.359l0.249 0.041h6.72c24.050 5.126 51.682 8.062 80 8.062s55.949-2.936 82.608-8.519l-2.608 0.457h6.72c18.258-3.568 31.867-19.375 32-38.386v-0.014zM422.4 606.080h179.2c3.535 0 6.4-2.865 6.4-6.4v-99.2c0-3.535-2.865-6.4-6.4-6.4h-179.2c-3.535 0-6.4 2.865-6.4 6.4v99.2c0 3.535 2.865 6.4 6.4 6.4z" />
<glyph unicode="&#xe914;" glyph-name="icon-T1-buttons" d="M603.2 694.4h-6.4c-25.494 5.341-54.79 8.398-84.8 8.398s-59.305-3.058-87.592-8.879l2.792 0.48h-6.72c-30.053-5.643-52.489-31.68-52.489-62.956 0-0.367 0.003-0.733 0.009-1.099l-0.001 0.055v-234.88c0.075-40.921 11.238-79.22 30.643-112.071l-0.563 1.031 35.2-60.48c11.655-19.297 32.515-32.001 56.342-32.001 0.105 0 0.209 0 0.314 0.001h44.144c0.359-0.007 0.783-0.011 1.208-0.011 23.569 0 44.162 12.74 55.269 31.709l0.164 0.302 36.16 64c18.152 31.468 28.933 69.175 29.12 109.385v233.015c0.005 0.293 0.008 0.639 0.008 0.986 0 31.391-22.599 57.503-52.416 62.954l-0.392 0.059zM629.76 396.8c-0.193-35.364-9.792-68.446-26.418-96.923l0.498 0.923-35.84-64c-6.868-11.865-19.463-19.742-33.906-19.84h-44.174c-14.469 0.112-27.111 7.827-34.139 19.343l-34.981 61.297c-16.687 28.041-26.553 61.827-26.56 97.918v234.882c0 19.072 13.676 34.95 31.757 38.362l0.243 0.038h6.72c24.050 5.126 51.682 8.062 80 8.062s55.949-2.936 82.608-8.519l-2.608 0.457h6.72c18.324-3.45 32-19.328 32-38.4v0zM422.4 606.080h179.2c3.535 0 6.4-2.865 6.4-6.4v-99.2c0-3.535-2.865-6.4-6.4-6.4h-179.2c-3.535 0-6.4 2.865-6.4 6.4v99.2c0 3.535 2.865 6.4 6.4 6.4zM531.2 469.76h70.4c3.535 0 6.4-2.865 6.4-6.4v-28.8c0-3.535-2.865-6.4-6.4-6.4h-70.4c-3.535 0-6.4 2.865-6.4 6.4v28.8c0 3.535 2.865 6.4 6.4 6.4zM422.4 468.8h70.4c3.535 0 6.4-2.865 6.4-6.4v-28.8c0-3.535-2.865-6.4-6.4-6.4h-70.4c-3.535 0-6.4 2.865-6.4 6.4v28.8c0 3.535 2.865 6.4 6.4 6.4z" />
<glyph unicode="&#xe915;" glyph-name="icon-T1-connect" d="M526.72 345.92h-29.44c-16.229 0.18-30.307 9.217-37.647 22.496l-0.113 0.224-23.36 41.92c-12.685 22.404-20.16 49.203-20.16 77.748 0 0.004 0 0.008 0 0.012v-0.001 164.16c-0.002 0.138-0.002 0.301-0.002 0.463 0 21.403 14.903 39.326 34.898 43.957l0.304 0.059h4.48c16.978 3.729 36.479 5.865 56.48 5.865s39.502-2.136 58.289-6.193l-1.809 0.327h4.48c20.14-4.81 34.883-22.655 34.883-43.943 0-0.189-0.001-0.377-0.003-0.565v0.029-163.52c0-0.064 0-0.139 0-0.214 0-28.079-7.233-54.468-19.937-77.407l0.417 0.821-24-43.84c-7.529-13.375-21.591-22.288-37.744-22.4h-0.016zM512 685.76c-0.097 0-0.212 0-0.327 0-18.693 0-36.923-1.981-54.492-5.745l1.699 0.305h-4.48c-12.188-2.865-21.122-13.645-21.122-26.512 0-0.129 0.001-0.258 0.003-0.387v0.020-164.48c0-0.094 0-0.204 0-0.315 0-25.018 6.525-48.512 17.966-68.873l-0.365 0.708 23.36-41.92c4.497-8.071 12.981-13.44 22.718-13.44 0.001 0 0.002 0 0.002 0h29.44c9.799 0.067 18.299 5.56 22.652 13.623l0.068 0.137 24 43.84c10.854 19.495 17.254 42.757 17.28 67.512v162.248c0.002 0.109 0.002 0.238 0.002 0.368 0 12.867-8.935 23.648-20.938 26.476l-0.184 0.037h-4.48c-15.736 3.739-33.906 6.053-52.557 6.396l-0.243 0.004zM452.48 635.52h119.040c2.474 0 4.48-2.006 4.48-4.48v-69.44c0-2.474-2.006-4.48-4.48-4.48h-119.040c-2.474 0-4.48 2.006-4.48 4.48v69.44c0 2.474 2.006 4.48 4.48 4.48zM540.48 303.68v-52.16h-18.24v-59.52h-19.84v59.52h-18.88v52.16h56.96z" />
<glyph unicode="&#xe916;" d="M551.36 178.24h-78.4c-0.225-0.006-0.489-0.010-0.754-0.010-10.242 0-19.207 5.468-24.135 13.643l-0.071 0.127-64 96c-29.426 43.619-46.99 97.353-47.040 155.187v233.613c0.36 22.875 18.988 41.281 41.915 41.281 0.114 0 0.228 0 0.342-0.001h263.342c0.097 0.001 0.211 0.001 0.325 0.001 22.927 0 41.555-18.406 41.915-41.248v-239.394c-0.289-59.994-19.423-115.463-51.779-160.855l0.579 0.855-60.48-86.080c-4.695-7.399-12.575-12.414-21.664-13.114l-0.096-0.006zM380.48 690.24c-0.1 0.003-0.217 0.004-0.334 0.004-7.552 0-13.716-5.946-14.064-13.413l-0.001-0.031v-233.92c0-0.010 0-0.023 0-0.036 0-52.093 15.777-100.502 42.813-140.708l-0.573 0.904 64-96h79.040l60.16 86.080c28.821 40.408 46.080 90.794 46.080 145.211 0 0.024 0 0.049 0 0.073v-0.004 238.4c-0.35 7.498-6.513 13.444-14.066 13.444-0.118 0-0.235-0.001-0.352-0.004h0.017z" />
<glyph unicode="&#xe917;" d="M407.040 624.32h209.92c4.595 0 8.32-3.725 8.32-8.32v-201.92c0-4.595-3.725-8.32-8.32-8.32h-209.92c-4.595 0-8.32 3.725-8.32 8.32v201.92c0 4.595 3.725 8.32 8.32 8.32z" />
<glyph unicode="&#xe918;" d="M539.52 352h-54.72c-6.578 0.052-12.387 3.298-15.96 8.261l-0.040 0.059-45.12 64c-20.029 28.726-32.002 64.366-32.002 102.802 0 0.309 0.001 0.617 0.002 0.925v-0.047 157.76c0.349 15.459 12.963 27.856 28.473 27.856 0.34 0 0.679-0.006 1.016-0.018l-0.049 0.001h184.32c0.288 0.010 0.627 0.016 0.967 0.016 15.51 0 28.124-12.398 28.473-27.824l0.001-0.032v-160c-0.074-40.641-13.522-78.128-36.176-108.308l0.336 0.468-42.24-57.28c-3.622-5.258-9.609-8.66-16.39-8.66-0.313 0-0.624 0.007-0.934 0.022l0.044-0.002zM419.84 694.4c-0.205 0.016-0.444 0.025-0.686 0.025-4.973 0-9.062-3.781-9.551-8.624l-0.003-0.040v-155.84c0.066-34.769 11.081-66.953 29.778-93.302l-0.338 0.502 45.12-64h56l42.24 57.28c19.839 26.468 31.828 59.817 32 95.96v160.040c-0.492 4.884-4.582 8.665-9.554 8.665-0.241 0-0.481-0.009-0.717-0.026l0.032 0.002z" />
<glyph unicode="&#xe919;" d="M438.4 650.88h146.88c3.181 0 5.76-2.579 5.76-5.76v-134.4c0-3.181-2.579-5.76-5.76-5.76h-146.88c-3.181 0-5.76 2.579-5.76 5.76v134.4c0 3.181 2.579 5.76 5.76 5.76z" />
<glyph unicode="&#xe91a;" d="M541.12 300.48v-50.56h-18.56v-57.92h-20.48v57.92h-18.88v50.56h57.92z" />
<glyph unicode="&#xe91b;" glyph-name="icon-top" d="M677.44 346.24c-3.255-1.423-7.047-2.252-11.033-2.252-0.284 0-0.566 0.004-0.848 0.013l0.041-0.001c-8.323 0.531-15.657 4.371-20.77 10.206l-0.030 0.034-93.44 109.44c-0.378 0.735-1.131 1.229-1.999 1.229-1.237 0-2.24-1.003-2.24-2.24 0-0.209 0.029-0.412 0.083-0.605l-0.004 0.016v-233.28c0.102-0.987 0.16-2.132 0.16-3.291 0-18.733-15.187-33.92-33.92-33.92s-33.92 15.187-33.92 33.92c0 1.159 0.058 2.304 0.172 3.433l-0.012-0.142v236.16c0.050 0.177 0.079 0.379 0.079 0.589 0 1.237-1.003 2.24-2.24 2.24-0.868 0-1.621-0.494-1.993-1.216l-0.006-0.013-88.32-104.32c-5.204-6.343-13.042-10.358-21.819-10.358-7.711 0-14.699 3.099-19.784 8.121l0.003-0.003c-6.16 5.845-9.993 14.090-9.993 23.231 0 8.17 3.062 15.625 8.101 21.28l-0.028-0.032 146.56 173.44c5.311 6.15 13.061 10.069 21.731 10.24h0.029c8.727-0.036 16.523-3.991 21.724-10.196l0.036-0.044 152-178.56c5.441-6.124 8.764-14.234 8.764-23.121 0-12.698-6.785-23.81-16.927-29.911l-0.157-0.088zM329.28 667.2c-0.024 0.488-0.038 1.060-0.038 1.635 0 18.891 14.881 34.306 33.561 35.163l0.077 0.003h292.48c18.795-1.81 33.372-17.523 33.372-36.64s-14.577-34.83-33.222-36.628l-0.15-0.012h-292.48c-18.751 0.866-33.625 16.278-33.625 35.165 0 0.463 0.009 0.923 0.027 1.381l-0.002-0.066z" />
<glyph unicode="&#xe91c;" glyph-name="icon-check" d="M692.8 646.080l-1.92 1.92c-6.246 7.057-15.326 11.484-25.44 11.484s-19.194-4.427-25.409-11.448l-0.031-0.036-196.48-224-3.84-1.6-3.84 1.92-48.64 57.28c-7.010 7.905-17.193 12.862-28.533 12.862-21.031 0-38.080-17.049-38.080-38.080 0-7.495 2.165-14.485 5.905-20.377l-0.092 0.155 100.8-148.16c5.391-8.036 14.386-13.292 24.618-13.44h8.662c17.251 0.146 32.385 9.075 41.163 22.529l0.117 0.191 195.2 296.32c4.473 6.632 7.141 14.803 7.141 23.597 0 11.162-4.297 21.32-11.326 28.911l0.025-0.028z" />
<glyph unicode="&#xe91d;" glyph-name="icon-error" d="M693.12 629.12c-46.317 46.267-110.276 74.88-180.919 74.88-141.385 0-256-114.615-256-256s114.615-256 256-256c70.642 0 134.602 28.613 180.921 74.882l-0.002-0.002c46.387 46.337 75.081 110.377 75.081 181.12s-28.694 134.783-75.079 181.118l-0.002 0.002zM494.080 615.68h53.12c16 0 18.24-9.28 18.24-14.72v-10.24l-10.88-194.56c0-14.4-8-17.28-18.88-17.28h-28.16c-10.56 0-17.28 2.88-18.88 17.92l-10.88 193.92v10.56c-1.28 4.8 2.24 14.080 16.32 14.080zM521.28 242.24c-0.095-0.001-0.207-0.001-0.319-0.001-27.747 0-50.24 22.493-50.24 50.24s22.493 50.24 50.24 50.24c27.747 0 50.24-22.493 50.24-50.24 0-0.112 0-0.224-0.001-0.336v0.017c0 0 0-0.001 0-0.001 0-27.634-22.311-50.057-49.903-50.239h-0.017z" />
<glyph unicode="&#xe91e;" glyph-name="icon-eye" d="M512.64 592c-99.2 0-198.4-45.76-256.64-136.64 128.64-200 394.56-203.84 512 1.28-57.92 90.24-156.48 135.36-255.36 135.36zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 60.16 51.52 109.12 115.84 109.12 64 0 115.84-48.64 115.84-108.8 0.32-60.16-51.52-108.8-115.84-108.8zM574.72 451.84c2.56-30.080-21.12-56.32-53.12-58.88-32-2.24-59.84 19.84-62.72 49.92-2.56 30.080 21.44 56.32 53.12 58.56 32 2.56 59.84-19.84 62.72-49.6z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 211 61" style="enable-background:new 0 0 211 61;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;}
</style>
<path id="unlocked" class="st0" d="M12,15v-2.6c0-2.1,1.7-3.8,3.7-3.8h1.1c2,0,3.7,1.7,3.7,3.8v0.2h-2.1c-0.1-1.1-1-1.9-2.1-1.9
c-1.2,0-2.1,1-2.1,2.2V15h6.8c0.6,0,1,0.5,1,1v6.5c0,0.6-0.5,1-1,1h-9.8c-0.6,0-1-0.5-1-1V16c0-0.6,0.5-1,1-1H12z"/>
<path id="locked" class="st0" d="M50.9,15h-0.5v-2.6c0-2.1-1.7-3.8-3.7-3.8h-1.1c-2,0-3.7,1.7-3.7,3.8V15h-0.9c-0.6,0-1,0.5-1,1v6.5
c0,0.6,0.5,1,1,1h9.8c0.6,0,1-0.5,1-1V16C51.9,15.4,51.5,15,50.9,15z M44.1,12.8c0-1.2,0.9-2.2,2.1-2.2c1.1,0,2,1,2.1,2.1V15h-4.2
V12.8z"/>
<g id="refresh">
<path d="M101,14.9c0.5-2.4,2.7-4.2,5.3-4.2c1,0,1.9,0.3,2.7,0.7c0.2,0.1,0.4,0.3,0.6,0.4l-2.1,2l5.8,1l-1-5.8l-1.2,1.2
c-0.2-0.1-0.4-0.3-0.6-0.4c-1.2-0.8-2.7-1.3-4.3-1.3c-3.7,0-6.8,2.6-7.4,6.2l-0.1,0.6h2.2L101,14.9z"/>
<path d="M111,17.2c-0.5,2.4-2.7,4.2-5.3,4.2c-1,0-1.9-0.3-2.7-0.7c-0.2-0.1-0.4-0.3-0.6-0.4l2.1-2l-5.8-1l1,5.8l1.2-1.2
c0.2,0.1,0.4,0.3,0.6,0.4c1.2,0.8,2.7,1.3,4.3,1.3c3.7,0,6.8-2.6,7.4-6.2l0.1-0.6h-2.2L111,17.2z"/>
</g>
<path id="settings" d="M142.7,14.5h-2.1c-0.1-0.4-0.3-0.8-0.5-1.1l1.4-1.4c0.3-0.3,0.3-0.9,0-1.2l-0.6-0.6c-0.3-0.3-0.9-0.3-1.2,0
l-1.4,1.4c-0.4-0.2-0.8-0.4-1.2-0.5V9.3c0-0.5-0.4-0.8-0.8-0.8h-0.8c-0.5,0-0.8,0.4-0.8,0.8v1.8c-0.5,0.1-0.9,0.3-1.3,0.6l-1.2-1.2
c-0.3-0.3-0.9-0.3-1.2,0l-0.6,0.6c-0.3,0.3-0.3,0.9,0,1.2l1.2,1.2c-0.3,0.4-0.4,0.9-0.6,1.4h-1.6c-0.5,0-0.8,0.4-0.8,0.8v0.8
c0,0.5,0.4,0.8,0.8,0.8h1.7c0.2,0.5,0.4,1,0.7,1.4l-1.1,1.1c-0.3,0.3-0.3,0.9,0,1.2l0.6,0.6c0.3,0.3,0.9,0.3,1.2,0l1.3-1.3
c0.4,0.2,0.9,0.4,1.4,0.4v1.7c0,0.5,0.4,0.8,0.8,0.8h0.8c0.5,0,0.8-0.4,0.8-0.8v-2c0.4-0.1,0.8-0.3,1.2-0.6l1.3,1.3
c0.3,0.3,0.9,0.3,1.2,0l0.6-0.6c0.3-0.3,0.3-0.9,0-1.2l-1.5-1.5c0.2-0.4,0.3-0.8,0.4-1.2h2c0.5,0,0.8-0.4,0.8-0.8v-0.8
C143.5,14.9,143.1,14.5,142.7,14.5z M135.8,18.1c-1.2,0-2.1-0.9-2.1-2.1c0-1.2,0.9-2.1,2.1-2.1c1.2,0,2.1,0.9,2.1,2.1
C137.9,17.2,136.9,18.1,135.8,18.1z"/>
<g id="eject">
<path d="M69.5,22.4h12.9c0.3,0,0.5-0.2,0.5-0.5V20c0-0.3-0.2-0.5-0.5-0.5H69.5c-0.3,0-0.5,0.2-0.5,0.5v1.9
C69,22.2,69.2,22.4,69.5,22.4z"/>
<path d="M75.8,9.7l-6.3,7.2c-0.2,0.2,0,0.5,0.2,0.5h12.6c0.3,0,0.4-0.3,0.2-0.5l-6.3-7.2C76.1,9.5,75.9,9.5,75.8,9.7z"/>
</g>
<path id="redirect" d="M76,38.5c-4.1,0-7.5,3.4-7.5,7.5c0,4.1,3.4,7.5,7.5,7.5s7.5-3.4,7.5-7.5C83.5,41.9,80.1,38.5,76,38.5z
M76.5,49.5v-2c-3-1-4,0-5,2c0-5,3-6,5-6v-2l4,4L76.5,49.5z"/>
<path id="warning" d="M113.5,51.9l-7-12.3c-0.3-0.5-0.7-0.5-1,0l-7,12.3c-0.3,0.5,0,0.9,0.5,0.9h14
C113.6,52.7,113.8,52.4,113.5,51.9z M107,50.2c0,0.3-0.2,0.5-0.5,0.5h-1c-0.3,0-0.5-0.2-0.5-0.5v-1c0-0.3,0.2-0.5,0.5-0.5h1
c0.3,0,0.5,0.2,0.5,0.5V50.2z M107,47.2c0,0.3-0.2,0.5-0.5,0.5h-1c-0.3,0-0.5-0.2-0.5-0.5v-3c0-0.3,0.2-0.5,0.5-0.5h1
c0.3,0,0.5,0.2,0.5,0.5V47.2z"/>
<path id="chat" d="M48,38.5h-4c-3,0-5.5,2.5-5.5,5.6c0,2.8,2,5.2,4.7,5.6v3.8l3.8-3.8H48c3,0,5.5-2.5,5.5-5.6
C53.5,41,51,38.5,48,38.5z"/>
<path id="info" d="M21.3,40.7c-2.9-2.9-7.7-2.9-10.6,0c-2.9,2.9-2.9,7.7,0,10.6c2.9,2.9,7.7,2.9,10.6,0
C24.2,48.4,24.2,43.6,21.3,40.7z M15.9,39.8c0.8,0,1.4,0.6,1.4,1.4c0,0.8-0.6,1.4-1.4,1.4c-0.8,0-1.5-0.7-1.5-1.4
C14.4,40.4,15.1,39.8,15.9,39.8z M18.2,50.4c0,0.3-0.1,0.4-0.4,0.4H14c-0.3,0-0.4-0.1-0.4-0.4v-0.9c0-0.3,0.1-0.4,0.4-0.4h0.7v-4H14
c-0.3,0-0.4-0.1-0.4-0.4v-0.9c0-0.3,0.1-0.4,0.4-0.4h2.6c0.3,0,0.4,0.1,0.4,0.4V49h0.7c0.3,0,0.4,0.1,0.4,0.4V50.4z"/>
<g id="close">
<g>
<path d="M143.1,51.2c0.5,0.5,0.5,1.4,0,1.9c-0.3,0.3-0.6,0.4-1,0.4c-0.4,0-0.7-0.1-0.9-0.4l-5.2-5.2l-5.2,5.2
c-0.5,0.5-1.4,0.5-1.9,0c-0.5-0.5-0.5-1.4,0-1.9l5.2-5.2l-5.2-5.2c-0.5-0.5-0.5-1.4,0-1.9c0.5-0.5,1.4-0.5,1.9,0l5.2,5.2l5.2-5.2
c0.5-0.5,1.4-0.5,1.9,0c0.5,0.5,0.5,1.4,0,1.9l-5.2,5.2L143.1,51.2z"/>
</g>
</g>
<g id="arrow-down">
<g>
<g>
<path d="M173.5,13.2l-6.8,7.4c-0.3,0.3-0.6,0.4-1,0.4c-0.4,0-0.7-0.2-1-0.4l-6.4-7.1c-0.5-0.5-0.4-1.3,0.1-1.8
c0.5-0.5,1.3-0.4,1.8,0.1l5.5,6l5.8-6.4c0.2-0.3,0.6-0.4,0.9-0.4c0.4,0,0.7,0.1,0.9,0.3C174,11.8,174,12.6,173.5,13.2z"/>
</g>
</g>
</g>
<g id="arrow-left">
<g>
<g>
<path d="M170.8,52.2c0,0.3-0.1,0.7-0.3,0.9c-0.2,0.3-0.5,0.4-0.9,0.4h-0.1c-0.3,0-0.6-0.1-0.8-0.3l-7-6.4
c-0.3-0.2-0.4-0.6-0.4-0.9c0-0.3,0.2-0.7,0.4-0.9l6.7-6.1c0.3-0.2,0.6-0.3,0.9-0.3c0.3,0,0.6,0.2,0.9,0.4
c0.5,0.5,0.4,1.3-0.1,1.8l-5.7,5.2l6,5.5C170.6,51.6,170.7,51.9,170.8,52.2z"/>
</g>
</g>
</g>
<g id="arrow-right">
<g>
<g>
<path d="M191.2,39.8c0-0.3,0.1-0.7,0.3-0.9c0.2-0.3,0.5-0.4,0.9-0.4h0.1c0.3,0,0.6,0.1,0.8,0.3l7,6.4c0.3,0.2,0.4,0.6,0.4,0.9
c0,0.3-0.2,0.7-0.4,0.9l-6.7,6.1c-0.3,0.2-0.6,0.3-0.9,0.3c-0.3,0-0.6-0.2-0.9-0.4c-0.5-0.5-0.4-1.3,0.1-1.8l5.7-5.2l-6-5.5
C191.4,40.4,191.3,40.1,191.2,39.8z"/>
</g>
</g>
</g>
<g id="arrow-up">
<g>
<g>
<path d="M189.8,20.8c-0.3,0-0.7-0.1-0.9-0.3c-0.3-0.2-0.4-0.5-0.4-0.9v-0.1c0-0.3,0.1-0.6,0.3-0.8l6.4-7c0.2-0.3,0.6-0.4,0.9-0.4
c0.3,0,0.7,0.2,0.9,0.4l6.1,6.7c0.2,0.3,0.3,0.6,0.3,0.9c0,0.3-0.2,0.6-0.4,0.9c-0.5,0.5-1.3,0.4-1.8-0.1l-5.2-5.7l-5.5,6
C190.5,20.6,190.1,20.8,189.8,20.8z"/>
</g>
</g>
</g>
<view id="icon-external" viewBox="30 60 30 30" />
<view id="icon-heart-view" viewBox="0 32 32 32" />
<view id="icon-arrow-right-view" viewBox="0 64 32 32" />
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0"
id="trezor" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" inkscape:export-ydpi="300" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" inkscape:export-xdpi="300" inkscape:export-filename="/home/stick/work/bitcoin/share-bitkey/logo/logo-black-horiz-2.png" sodipodi:docname="logo-black-horiz-2.svg" inkscape:version="0.48.4 r9939" xmlns:cc="http://creativecommons.org/ns#"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="auto" height="auto" fill="#FFFFFF"
viewBox="0 0 685.984 210" enable-background="new 0 0 685.984 210" xml:space="preserve">
<sodipodi:namedview fit-margin-bottom="20" fit-margin-right="20" fit-margin-top="20" fit-margin-left="20" gridtolerance="10" inkscape:current-layer="svg2" inkscape:window-maximized="1" inkscape:window-y="25" inkscape:window-x="0" inkscape:cx="274.79362" inkscape:window-height="1129" inkscape:cy="-36.370891" showgrid="false" inkscape:window-width="1920" inkscape:pageshadow="2" inkscape:zoom="0.76280916" inkscape:pageopacity="0" guidetolerance="10" pagecolor="#ffffff" bordercolor="#666666" objecttolerance="10" borderopacity="1" id="namedview30">
</sodipodi:namedview>
<rect id="rect25" x="320.944" y="717.998" display="none" fill="#FFFFFF" stroke="#000000" width="11" height="35"/>
<path id="path7" inkscape:connector-curvature="0" d="M97.879,19.707c-23.563,0-42.667,19.103-42.667,42.667v15.953
c-8.269,1.479-16.614,3.522-16.614,6.133v83.586c0,0,0,2.344,2.553,3.359c9.427,3.751,46.438,16.869,54.932,19.873
c1.119,0.396,1.355,0.385,1.743,0.385c0.408,0,0.619,0.014,1.729-0.379c8.478-2.997,45.617-16.148,54.985-19.857
c2.433-0.963,2.503-3.347,2.503-3.347V84.464c0-2.61-8.229-4.658-16.499-6.138V62.373C140.545,38.81,121.442,19.707,97.879,19.707z
M97.879,40.098c13.86,0,22.292,8.416,22.292,22.276v13.898c-15.649-1.14-28.947-1.14-44.598,0V62.374
C75.573,48.513,84.017,40.098,97.879,40.098z M97.824,96.79c19.389,0.031,35.688,1.475,35.688,4.235c0,0,0,51.321,0,52.08
c0,0.76-0.12,0.888-0.841,1.161c-0.72,0.274-33.149,11.993-33.149,11.993s-1.331,0.442-1.697,0.442c-0.415,0-1.747-0.46-1.747-0.46
s-32.415-11.708-33.143-11.983c-0.729-0.274-0.823-0.416-0.823-1.182c0-0.844,0-52.053,0-52.053
C62.112,98.188,78.436,96.759,97.824,96.79z"/>
<g id="g3222" transform="matrix(0.95575135,0,0,0.95575135,91.363135,-287.43374)">
<path id="path13" inkscape:connector-curvature="0" d="M126.229,461.703v-59.704h-21.763v-21.4h67.972v21.4h-21.646v59.704H126.229
z"/>
<path id="path15" inkscape:connector-curvature="0" d="M227.522,461.703l-11.185-25.534h-8.877v25.534h-24.561v-81.104h43.286
c18.967,0,29.184,12.645,29.184,27.844c0,13.987-8.148,21.401-14.591,24.563l14.714,28.696h-27.969V461.703z M230.44,408.32
c0-4.375-3.891-6.323-8.026-6.323h-14.957v12.767h14.957C226.549,414.763,230.44,412.822,230.44,408.32z"/>
<path id="path17" inkscape:connector-curvature="0" d="M268.232,461.703v-81.104h61.527v21.4h-36.965v8.026h36.115v21.403h-36.115
v8.873h36.965v21.402H268.232z"/>
<path id="path19" inkscape:connector-curvature="0" d="M342.547,461.718v-19.454l30.883-40.249h-30.883v-21.4h63.349v19.333
l-31.007,40.37h31.857v21.4h-64.199V461.718z"/>
<path id="path21" inkscape:connector-curvature="0" d="M412.762,421.229c0-24.929,19.331-41.952,44.381-41.952
c25.046,0,44.383,17.023,44.383,41.952c0,24.925-19.337,41.948-44.383,41.948C432.093,463.177,412.762,446.154,412.762,421.229z
M476.599,421.229c0-11.311-7.66-20.308-19.456-20.308c-11.794,0-19.457,8.998-19.457,20.308c0,11.307,7.661,20.303,19.457,20.303
C468.937,441.532,476.599,432.534,476.599,421.229z"/>
<path id="path23" inkscape:connector-curvature="0" d="M556.611,461.718l-11.182-25.534h-8.879v25.534h-24.563v-81.103h43.289
c18.971,0,29.185,12.645,29.185,27.843c0,13.987-8.149,21.401-14.594,24.565l14.713,28.696H556.611z M559.531,408.336
c0-4.376-3.89-6.323-8.026-6.323h-14.957v12.767h14.957C555.638,414.781,559.531,412.838,559.531,408.336z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>TrezorConnect boilerplate with React | TREZOR</title>
<title>Ethereum Wallet| TREZOR</title>
<meta name="title" content="TrezorConnect" />
<meta name="description" content="TrezorConnect" />
<meta name="keywords" content="TrezorConnect" />

@ -0,0 +1,106 @@
/* @flow */
'use strict';
import * as ACCOUNT from './constants/account';
import { initialState } from '../reducers/AccountDetailReducer';
import { findSelectedDevice } from '../reducers/TrezorConnectReducer';
import type { State } from '../reducers/AccountDetailReducer';
import type { Discovery } from '../reducers/DiscoveryReducer';
export const init = (): any => {
return (dispatch, getState): void => {
const { location } = getState().router;
const urlParams = location.params;
const selected = findSelectedDevice( getState().connect );
if (!selected) return;
const state: State = {
index: parseInt(urlParams.address),
checksum: selected.checksum,
coin: urlParams.coin,
location: location.pathname
};
dispatch({
type: ACCOUNT.INIT,
state: state
});
// let discoveryProcess: ?Discovery = getState().discovery.find(d => d.checksum === selected.checksum && d.coin === currentAccount.coin);
// const discovering: boolean = (!discoveryProcess || !discoveryProcess.completed);
// const state: State = {
// ...initialState,
// loaded: true,
// checksum: currentAccount.checksum,
// address: currentAccount.address,
// coin: urlParams.coin,
// balance: currentAccount.balance,
// discovering
// };
// dispatch({
// type: ACCOUNT.INIT,
// state
// });
}
}
export const update = (newProps: any): any => {
return (dispatch, getState): void => {
const {
accountDetail,
connect,
discovery,
accounts,
router
} = getState();
const isLocationChanged: boolean = newProps.location.pathname !== accountDetail.location;
if (isLocationChanged) {
dispatch({
type: ACCOUNT.INIT,
state: {
...accountDetail,
location: newProps.location.pathname,
}
});
return;
}
// update comes from device
// const device = connect.devices.find(d => d.checksum === accountDetail.checksum);
// if (accountDetail.detail !== device) {
// console.warn("DEV UPDATE!!!!")
// }
// const discoveryProcess = discovery.find(d => d.checksum === device.checksum && d.coin === accountDetail.coin);
// const account = accounts.find(a => a.checksum === accountDetail.checksum && a.index === accountDetail.addressIndex && a.coin === accountDetail.coin);
// if (account && !accountDetail.address) {
// // update current address
// console.warn("ACC UPDATE!!!!")
// }
// isDeviceChanged
// isDiscoveryChanged
}
}
export const dispose = (device: any): any => {
return (dispatch, getState): void => {
dispatch({
type: ACCOUNT.DISPOSE,
});
}
}

@ -0,0 +1,33 @@
/* @flow */
'use strict';
export const ON_RESIZE: string = 'ON_RESIZE';
export const ON_BEFORE_UNLOAD: string = 'app__on_before_unload';
export const TOGGLE_DEVICE_DROPDOWN: string = 'TOGGLE_DEVICE_DROPDOWN';
export const RESIZE_CONTAINER: string = 'RESIZE_CONTAINER';
export const onResize = (): any => {
return {
type: ON_RESIZE
}
}
export const onBeforeUnload = (): any => {
return {
type: ON_BEFORE_UNLOAD
}
}
export const resizeAppContainer = (opened: boolean): any => {
return {
type: RESIZE_CONTAINER,
opened
}
}
export const toggleDeviceDropdown = (opened: boolean): any => {
return {
type: TOGGLE_DEVICE_DROPDOWN,
opened
}
}

@ -1,10 +0,0 @@
/* @flow */
'use strict';
export const ON_RESIZE: string = 'ON_RESIZE';
export const onResize = (): void => {
return {
type: ON_RESIZE
}
}

@ -0,0 +1,112 @@
/* @flow */
'use strict';
import * as CONNECT from './constants/TrezorConnect';
import * as ADDRESS from './constants/Address';
import * as TOKEN from './constants/Token';
import * as DISCOVERY from './constants/Discovery';
import * as STORAGE from './constants/LocalStorage';
import { httpRequest } from '../utils/networkUtils';
export function loadData(): any {
return async (dispatch, getState) => {
// check if local storage is available
// let available: boolean = true;
// if (typeof window.localStorage === 'undefined') {
// available = false;
// } else {
// try {
// window.localStorage.setItem('ethereum_wallet', true);
// } catch (error) {
// available = false;
// }
// }
dispatch( loadTokensFromJSON() );
}
}
export function loadTokensFromJSON(): any {
return async (dispatch, getState) => {
try {
const appConfig = await httpRequest('data/appConfig.json', 'json');
const ethTokens = await httpRequest('data/ethTokens.json', 'json');
const ethERC20 = await httpRequest('data/ethERC20.json', 'json');
const devices: ?string = get('devices');
console.log("GET23", JSON.parse(devices))
if (devices) {
dispatch({
type: CONNECT.DEVICE_FROM_STORAGE,
payload: JSON.parse(devices)
})
}
const accounts: ?string = get('accounts');
if (accounts) {
dispatch({
type: ADDRESS.FROM_STORAGE,
payload: JSON.parse(accounts)
})
}
const tokens: ?string = get('tokens');
if (tokens) {
dispatch({
type: TOKEN.FROM_STORAGE,
payload: JSON.parse(tokens)
})
}
const discovery: ?string = get('discovery');
if (discovery) {
dispatch({
type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(discovery)
})
}
dispatch({
type: STORAGE.READY,
appConfig,
ethTokens,
ethERC20
})
} catch(error) {
dispatch({
type: STORAGE.ERROR,
error
})
}
}
}
export const save = (key: string, value: string): any => {
return (dispatch, getState) => {
if (typeof window.localStorage !== 'undefined') {
//console.log("SAVEE!!!!", key, value)
try {
window.localStorage.setItem(key, value);
} catch (error) {
// available = false;
console.error("ERROR: " + error)
}
}
}
}
export const get = (key: string): ?string => {
if (typeof window.localStorage !== 'undefined') {
try {
console.log("GETTT", JSON.parse(window.localStorage.getItem(key)))
return window.localStorage.getItem(key);
} catch (error) {
// available = false;
return null;
}
}
}

@ -3,125 +3,75 @@
import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect';
import * as ACTIONS from './index';
import * as MODAL from './constants/Modal';
import * as CONNECT from './constants/TrezorConnect';
export function onPinAdd(number: number): any {
return {
type: ACTIONS.ON_PIN_ADD,
number
}
}
export function onPinBackspace(): any {
return {
type: ACTIONS.ON_PIN_BACKSPACE
}
}
export function onPinSubmit(value: string): void {
export function onPinSubmit(value: string): any {
TrezorConnect.uiMessage({ type: UI.RECEIVE_PIN, data: value });
return {
type: ACTIONS.CLOSE_MODAL
}
}
export function onPassphraseChange(value: string): any {
return {
type: ACTIONS.ON_PASSPHRASE_CHANGE,
value
}
}
export function onPassphraseShow(): any {
return {
type: ACTIONS.ON_PASSPHRASE_SHOW
}
}
export function onPassphraseHide(): any {
return {
type: ACTIONS.ON_PASSPHRASE_HIDE
}
}
export function onPassphraseSave(): any {
return {
type: ACTIONS.ON_PASSPHRASE_SAVE
export function onPassphraseSubmit(passphrase: string): any {
return async (dispatch, getState): Promise<void> => {
const resp = await TrezorConnect.uiMessage({
type: UI.RECEIVE_PASSPHRASE,
data: {
value: passphrase,
save: true
}
});
dispatch({
type: ACTIONS.CLOSE_MODAL
});
}
}
export function onPassphraseForget(): any {
export const askForRemember = (device: any) => {
return {
type: ACTIONS.ON_PASSPHRASE_FORGET
type: MODAL.REMEMBER,
device
}
}
export function onPassphraseFocus(): any {
export const onRememberDevice = (device: any) => {
return {
type: ACTIONS.ON_PASSPHRASE_FOCUS
type: CONNECT.REMEMBER,
device
}
}
export function onPassphraseBlur(): any {
export const onForgetDevice = (device: any) => {
return {
type: ACTIONS.ON_PASSPHRASE_BLUR
type: CONNECT.FORGET,
device,
}
}
export function onPassphraseSubmit(value: string, cache: boolean): void {
TrezorConnect.uiMessage({
type: UI.RECEIVE_PASSPHRASE,
data: {
value,
save: cache
}
});
export const onForgetSingleDevice = (device: any) => {
return {
type: ACTIONS.CLOSE_MODAL
type: CONNECT.FORGET_SINGLE,
device,
}
}
export function onConfirmation(): any {
//postMessage(new UiMessage(UI.RECEIVE_CONFIRMATION, 'true') );
TrezorConnect.uiMessage({
type: UI.RECEIVE_CONFIRMATION,
data: 'true'
});
export const onCancel = () => {
return {
type: ACTIONS.CLOSE_MODAL
}
}
export function onConfirmationCancel(): any {
TrezorConnect.uiMessage({
type: UI.RECEIVE_CONFIRMATION,
data: 'false'
});
return {
type: ACTIONS.CLOSE_MODAL
}
}
export const onDuplicateDevice = (device: any): any => {
return (dispatch: any, getState: any): void => {
export function onPermissionGranted(): any {
//postMessage(new UiMessage(UI.RECEIVE_CONFIRMATION, 'true') );
TrezorConnect.uiMessage({
type: UI.RECEIVE_PERMISSION,
data: 'true'
});
dispatch( onCancel() );
return {
type: ACTIONS.CLOSE_MODAL
}
}
export function onPermissionRejected(): any {
TrezorConnect.uiMessage({
type: UI.RECEIVE_PERMISSION,
data: 'false'
});
return {
type: ACTIONS.CLOSE_MODAL
dispatch({
type: CONNECT.DUPLICATE,
device
});
}
}

@ -0,0 +1,110 @@
/* @flow */
'use strict';
import TrezorConnect from 'trezor-connect';
import * as RECEIVE from './constants/receive';
import * as NOTIFICATION from './constants/notification';
import { initialState } from '../reducers/ReceiveReducer';
import type { State } from '../reducers/ReceiveReducer';
import { findSelectedDevice } from '../reducers/TrezorConnectReducer';
export const init = (): any => {
return (dispatch, getState): void => {
const { location } = getState().router;
const urlParams = location.params;
const selected = findSelectedDevice( getState().connect );
if (!selected) return;
const state: State = {
...initialState,
checksum: selected.checksum,
accountIndex: parseInt(urlParams.address),
coin: urlParams.coin,
location: location.pathname,
};
dispatch({
type: RECEIVE.INIT,
state: state
});
}
}
export const update = (newProps: any): any => {
return (dispatch, getState): void => {
const {
receive,
router
} = getState();
const isLocationChanged: boolean = router.location.pathname !== receive.location;
if (isLocationChanged) {
dispatch( init() );
return;
}
}
}
export const dispose = (address: string): any => {
return {
type: RECEIVE.DISPOSE
}
}
export const showUnverifiedAddress = () => {
return {
type: RECEIVE.SHOW_UNVERIFIED_ADDRESS
}
}
export const showAddress = (address_n: string): any => {
return async (dispatch, getState) => {
const selected = findSelectedDevice(getState().connect);
if (selected && !selected.connected) {
dispatch({
type: RECEIVE.REQUEST_UNVERIFIED,
});
return;
}
const response = await TrezorConnect.ethereumGetAddress({
device: {
path: selected.path,
instance: selected.instance,
state: selected.checksum
},
address_n
});
if (response && response.success) {
dispatch({
type: RECEIVE.SHOW_ADDRESS
})
} else {
// TODO: handle invalid pin?
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Veryfying address error',
message: response.data.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(showAddress(address_n))
}
}
]
}
})
}
}
}

@ -1,80 +1,672 @@
/* @flow */
'use strict';
import * as ACTIONS from './index';
import { getNonce, estimateGas, getGasPrice, push } from './Web3Actions';
import * as SEND from './constants/SendForm';
import * as NOTIFICATION from './constants/notification';
import { getNonce, estimateGas, getGasPrice, pushTx } from './Web3Actions';
import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsUnits from 'ethereumjs-units';
import EthereumjsTx from 'ethereumjs-tx';
import TrezorConnect from 'trezor-connect';
import { strip } from '../utils/ethUtils';
import { push } from 'react-router-redux';
import BigNumber from 'bignumber.js';
import { initialState } from '../reducers/SendFormReducer';
import type { State, FeeLevel } from '../reducers/SendFormReducer';
import { findSelectedDevice } from '../reducers/TrezorConnectReducer';
export const onAddressChange = (address: string): void => {
return {
type: ACTIONS.ON_ADDRESS_CHANGE,
address
const numberRegExp = new RegExp('^([0-9]{0,10}\\.)?[0-9]{1,18}$');
const calculateFee = (gasPrice: string, gasLimit: string): string => {
return EthereumjsUnits.convert( new BigNumber(gasPrice).times(gasLimit), 'gwei', 'ether');
}
const calculateTotal = (amount: string, gasPrice: string, gasLimit: string): string => {
try {
return new BigNumber(amount).plus( calculateFee(gasPrice, gasLimit) ).toString();
} catch (error) {
return '0';
}
}
export const onAmountChange = (amount: string): void => {
return {
type: ACTIONS.ON_AMOUNT_CHANGE,
amount
export const calculateMaxAmount = (balance: string, gasPrice: string, gasLimit: string): string => {
try {
const fee = EthereumjsUnits.convert( new BigNumber(gasPrice).times(gasLimit), 'gwei', 'ether');
const b = new BigNumber(balance);
const max = b.minus(fee);
if (max.lessThan(0)) return '0';
return max.toString();
} catch (error) {
return '0';
}
}
export const onGasPriceChange = (gasPrice: string): void => {
return {
type: ACTIONS.ON_GAS_PRICE_CHANGE,
gasPrice
export const getFeeLevels = (coin: string, gasPrice: BigNumber | string, gasLimit: string): Array<FeeLevel> => {
if (typeof gasPrice === 'string') gasPrice = new BigNumber(gasPrice);
const quarter: BigNumber = gasPrice.dividedBy(4);
const high: string = gasPrice.plus(quarter.times(2)).toString();
const low: string = gasPrice.minus(quarter.times(2)).toString();
coin = coin.toUpperCase();
return [
{
value: 'High',
gasPrice: high,
label: `${ calculateFee(high, gasLimit) } ${ coin }`
},
{
value: 'Normal',
gasPrice: gasPrice.toString(),
label: `${ calculateFee(gasPrice.toString(), gasLimit) } ${ coin }`
},
{
value: 'Low',
gasPrice: low,
label: `${ calculateFee(low, gasLimit) } ${ coin }`
},
{
value: 'Custom',
gasPrice: low,
label: '',
},
]
}
export const findBalance = (getState: any): string => {
const state = getState().sendForm;
const account = getState().accounts.find(a => a.checksum === state.checksum && a.index === state.accountIndex && a.coin === state.coin);
if (state.token !== state.coin) {
return getState().tokens.find(t => t.ethAddress === account.address && t.symbol === state.token).balance;
} else {
return account.balance;
}
}
export const onGasLimitChange = (gasLimit: string): void => {
// initialize component
export const init = (): any => {
return (dispatch, getState): void => {
const { location } = getState().router;
const urlParams = location.params;
const selected = findSelectedDevice( getState().connect );
if (!selected) return;
const web3instance = getState().web3.find(w3 => w3.coin === urlParams.coin);
if (!web3instance) {
// no backend for this coin
//return;
}
// TODO: check if there are some unfinished tx in localStorage
const { config } = getState().localStorage;
const coin = config.coins.find(c => c.symbol === urlParams.coin);
const gasPrice: BigNumber = new BigNumber( EthereumjsUnits.convert(web3instance.gasPrice, 'wei', 'gwei') ) || new BigNumber(coin.defaultGasPrice);
const gasLimit: string = coin.defaultGasLimit.toString();
const feeLevels: Array<FeeLevel> = getFeeLevels(urlParams.coin, gasPrice, gasLimit);
// TODO: get nonce
const state: State = {
...initialState,
checksum: selected.checksum,
accountIndex: parseInt(urlParams.address),
coin: urlParams.coin,
token: urlParams.coin,
location: location.pathname,
feeLevels,
selectedFeeLevel: feeLevels.find(f => f.value === 'Normal'),
recommendedGasPrice: gasPrice.toString(),
gasLimit,
gasPrice: gasPrice.toString(),
nonce: '', // TODO!!!
};
dispatch({
type: SEND.INIT,
state
});
}
}
export const update = (): any => {
return (dispatch, getState): void => {
const {
sendForm,
router
} = getState();
const isLocationChanged: boolean = router.location.pathname !== sendForm.location;
if (isLocationChanged) {
dispatch( init() );
return;
}
}
}
export const dispose = (): any => {
return {
type: ACTIONS.ON_GAS_LIMIT_CHANGE,
gasLimit
type: SEND.DISPOSE
}
}
export const onTxDataChange = (data: string): void => {
export const toggleAdvanced = (address: string): any => {
return {
type: ACTIONS.ON_TX_DATA_CHANGE,
data
type: SEND.TOGGLE_ADVANCED
}
}
export const onSend = (addressId): void => {
return async (dispatch, getState) => {
const { web3 } = getState().web3;
const { address, amount } = getState().sendForm;
const { addresses } = getState().addresses;
export const validation = (): any => {
return (dispatch, getState): void => {
const state: State = getState().sendForm;
const errors: {[k: string]: string} = {};
const warnings: {[k: string]: string} = {};
const infos: {[k: string]: string} = {};
const currentAddress = addresses[addressId];
const address_n = currentAddress.path;
if (!state.untouched) {
// valid address
if (state.touched.address) {
const accounts = getState().accounts;
const myAccount = accounts.find(a => a.address.toLowerCase() === state.address.toLowerCase());
if (state.address.length < 1) {
errors.address = 'Address is not set';
} else if (!EthereumjsUtil.isValidAddress(state.address)) {
errors.address = 'Address is not valid';
} else if (myAccount) {
if (myAccount.coin === state.coin) {
infos.address = `TREZOR Address #${ (myAccount.index + 1) }`;
} else {
// TODO: load coins from config
warnings.address = `Looks like it's TREZOR address in Account #${ (myAccount.index + 1) } of ${ myAccount.coin.toUpperCase() }`;
}
}
}
// valid amount
// https://stackoverflow.com/a/42701461
//const regexp = new RegExp('^(?:[0-9]{0,10}\\.)?[0-9]{1,18}$');
if (state.touched.amount) {
if (state.amount.length < 1) {
errors.amount = 'Amount is not set';
} else if (state.amount.length > 0 && !state.amount.match(numberRegExp)) {
errors.amount = 'Amount is not a number';
} else {
const account = getState().accounts.find(a => a.checksum === state.checksum && a.index === state.accountIndex && a.coin === state.coin);
if (state.token !== state.coin) {
const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === state.token).balance;
if (new BigNumber(state.total).greaterThan(account.balance)) {
errors.amount = `Not enough ${ state.coin.toUpperCase() } to cover transaction fee`;
} else if (new BigNumber(state.amount).greaterThan(tokenBalance)) {
errors.amount = 'Not enough funds';
}
} else {
if (new BigNumber(state.total).greaterThan(account.balance)) {
errors.amount = 'Not enough funds';
}
}
}
}
// valid gas limit
if (state.touched.gasLimit) {
if (state.gasLimit.length < 1) {
errors.gasLimit = 'Gas limit is not set';
} else if (state.gasLimit.length > 0 && !state.gasLimit.match(numberRegExp)) {
errors.gasLimit = 'Gas limit is not a number';
} else {
const gl: BigNumber = new BigNumber(state.gasLimit);
if (gl.lessThan(1)) {
errors.gasLimit = 'Gas limit is too low';
} else if (gl.lessThan(1000)) {
warnings.gasLimit = 'Gas limit is below recommended';
}
}
}
// valid gas price
if (state.touched.gasPrice) {
if (state.gasPrice.length < 1) {
errors.gasPrice = 'Gas price is not set';
} else if (state.gasPrice.length > 0 && !state.gasPrice.match(numberRegExp)) {
errors.gasPrice = 'Gas price is not a number';
} else {
const gp: BigNumber = new BigNumber(state.gasPrice);
if (gp.greaterThan(100)) {
errors.gasPrice = 'Gas price is too high';
} else if (gp.lessThan(1)) {
errors.gasPrice = 'Gas price is too low';
}
}
}
// valid data
if (state.touched.data && state.coin === state.token && state.data.length > 0) {
const re = /^[0-9A-Fa-f]+$/g;
//const re = /^[0-9A-Fa-f]{6}$/g;
if (!re.test(state.data)) {
errors.data = 'Data is not valid hexadecimal';
}
}
// valid nonce?
dispatch({
type: SEND.VALIDATION,
errors,
warnings,
infos
});
}
}
}
export const onAddressChange = (address: string): any => {
return (dispatch, getState): void => {
const currentState: State = getState().sendForm;
const touched = { ...currentState.touched };
touched.address = true;
const state: State = {
...currentState,
untouched: false,
touched,
address
};
dispatch({
type: SEND.ADDRESS_CHANGE,
state
});
dispatch( validation() );
}
}
export const onAmountChange = (amount: string): any => {
return (dispatch, getState): void => {
const currentState: State = getState().sendForm;
const touched = { ...currentState.touched };
touched.amount = true;
const total: string = calculateTotal(currentState.token !== currentState.coin ? '0' : amount, currentState.gasPrice, currentState.gasLimit);
const state: State = {
...currentState,
untouched: false,
touched,
setMax: false,
amount,
total
};
dispatch({
type: SEND.AMOUNT_CHANGE,
state
});
dispatch( validation() );
}
}
export const onCurrencyChange = (currency: any): any => {
return (dispatch, getState): void => {
const currentState = getState().sendForm;
const account = getState().accounts.find(a => a.checksum === currentState.checksum && a.index === currentState.accountIndex && a.coin === currentState.coin);
if (!account) {
// account not found
return;
}
const { config } = getState().localStorage;
const coin = config.coins.find(c => c.symbol === currentState.coin);
let gasLimit: string = '';
let amount: string = currentState.amount;
let total: string;
if (currentState.coin !== currency.value) {
gasLimit = coin.defaultGasLimitTokens.toString();
if (currentState.setMax) {
const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === currency.value).balance;
amount = tokenBalance;
}
total = calculateTotal('0', currentState.gasPrice, currentState.gasLimit);
} else {
gasLimit = coin.defaultGasLimit.toString();
if (currentState.setMax) {
amount = calculateMaxAmount(account.balance, currentState.gasPrice, currentState.gasLimit);
}
total = calculateTotal(amount, currentState.gasPrice, currentState.gasLimit);
}
const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coin, currentState.gasPrice, gasLimit);
const state: State = {
...currentState,
token: currency.value,
amount,
total,
feeLevels,
selectedFeeLevel: feeLevels.find(f => f.value === currentState.selectedFeeLevel.value),
gasLimit,
};
dispatch({
type: SEND.CURRENCY_CHANGE,
state
});
dispatch( validation() );
}
}
export const onSetMax = (): any => {
return (dispatch, getState): void => {
const currentState = getState().sendForm;
const touched = { ...currentState.touched };
touched.amount = true;
const account = getState().accounts.find(a => a.checksum === currentState.checksum && a.index === currentState.accountIndex && a.coin === currentState.coin);
if (!account) {
// account not found
return;
}
let amount: string = currentState.amount;
let total: string = currentState.total;
if (!currentState.setMax) {
if (currentState.token !== currentState.coin) {
const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === currentState.token).balance;
amount = tokenBalance;
total = calculateTotal('0', currentState.gasPrice, currentState.gasLimit);
} else {
amount = calculateMaxAmount(account.balance, currentState.gasPrice, currentState.gasLimit);
total = calculateTotal(amount, currentState.gasPrice, currentState.gasLimit);
}
}
const state: State = {
...currentState,
untouched: false,
touched,
setMax: !currentState.setMax,
amount,
total
};
dispatch({
type: SEND.SET_MAX,
state
});
dispatch( validation() );
}
}
export const onFeeLevelChange = (feeLevel: any): any => {
return (dispatch, getState): void => {
const currentState = getState().sendForm;
const state: State = {
...currentState,
untouched: false,
selectedFeeLevel: feeLevel,
};
if (feeLevel.value === 'Custom') {
// TODO: update value for custom fee
state.advanced = true;
feeLevel.gasPrice = state.gasPrice;
feeLevel.label = `${ calculateFee(state.gasPrice, state.gasLimit) } ${ state.coin.toUpperCase() }`;
} else {
const customLevel = state.feeLevels.find(f => f.value === 'Custom');
customLevel.label = '';
state.gasPrice = feeLevel.gasPrice;
}
if (currentState.setMax) {
const account = getState().accounts.find(a => a.checksum === currentState.checksum && a.index === currentState.accountIndex && a.coin === currentState.coin);
if (state.token !== state.coin) {
const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === currentState.token).balance;
state.amount = tokenBalance;
} else {
state.amount = calculateMaxAmount(account.balance, state.gasPrice, state.gasLimit);
}
}
state.total = calculateTotal(state.token !== state.coin ? '0' : state.amount, state.gasPrice, state.gasLimit);
dispatch({
type: SEND.FEE_LEVEL_CHANGE,
state
});
dispatch( validation() );
}
}
export const updateFeeLevels = (): any => {
return (dispatch, getState): void => {
const currentState = getState().sendForm;
const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coin, currentState.recommendedGasPrice, currentState.gasLimit);
const state: State = {
...currentState,
feeLevels,
selectedFeeLevel: feeLevels.find(f => f.value === currentState.selectedFeeLevel.value),
gasPrice: currentState.recommendedGasPrice,
gasPriceNeedsUpdate: false,
};
if (currentState.setMax) {
const account = getState().accounts.find(a => a.checksum === currentState.checksum && a.index === currentState.accountIndex && a.coin === currentState.coin);
if (state.token !== state.coin) {
const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === currentState.token).balance;
state.amount = tokenBalance;
} else {
state.amount = calculateMaxAmount(account.balance, state.gasPrice, state.gasLimit);
}
}
state.total = calculateTotal(state.token !== state.coin ? '0' : state.amount, state.gasPrice, state.gasLimit);
dispatch({
type: SEND.UPDATE_FEE_LEVELS,
state
});
dispatch( validation() );
}
}
export const onGasPriceChange = (gasPrice: string): any => {
return (dispatch, getState): void => {
const currentState = getState().sendForm;
const touched = { ...currentState.touched };
touched.gasPrice = true;
const state: State = {
...currentState,
untouched: false,
touched,
gasPrice: gasPrice,
};
if (gasPrice.match(numberRegExp) && state.gasLimit.match(numberRegExp)) {
const customLevel = currentState.feeLevels.find(f => f.value === 'Custom');
customLevel.gasPrice = gasPrice;
customLevel.label = `${ calculateFee(gasPrice, state.gasLimit) } ${ state.coin.toUpperCase() }`;
state.selectedFeeLevel = customLevel;
if (currentState.setMax) {
const account = getState().accounts.find(a => a.checksum === currentState.checksum && a.index === currentState.accountIndex && a.coin === currentState.coin);
if (state.token !== state.coin) {
const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === currentState.token).balance;
state.amount = tokenBalance;
} else {
state.amount = calculateMaxAmount(account.balance, state.gasPrice, state.gasLimit);
}
}
}
state.total = calculateTotal(state.token !== state.coin ? '0' : state.amount, state.gasPrice, state.gasLimit);
dispatch({
type: SEND.GAS_PRICE_CHANGE,
state
});
dispatch( validation() );
}
}
export const onGasLimitChange = (gasLimit: string): any => {
return (dispatch, getState): void => {
const currentState = getState().sendForm;
const touched = { ...currentState.touched };
touched.gasLimit = true;
const state: State = {
...currentState,
untouched: false,
touched,
gasLimit,
};
if (gasLimit.match(numberRegExp) && state.gasPrice.match(numberRegExp)) {
const customLevel = currentState.feeLevels.find(f => f.value === 'Custom');
customLevel.label = `${ calculateFee(state.gasPrice, gasLimit) } ${ state.coin.toUpperCase() }`;
state.selectedFeeLevel = customLevel;
if (currentState.setMax) {
const account = getState().accounts.find(a => a.checksum === currentState.checksum && a.index === currentState.accountIndex && a.coin === currentState.coin);
if (state.token !== state.coin) {
const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === currentState.token).balance;
state.amount = tokenBalance;
} else {
state.amount = calculateMaxAmount(account.balance, state.gasPrice, state.gasLimit);
}
}
}
state.total = calculateTotal(state.token !== state.coin ? '0' : state.amount, state.gasPrice, state.gasLimit);
dispatch({
type: SEND.GAS_LIMIT_CHANGE,
state
});
dispatch( validation() );
}
}
export const onDataChange = (data: string): any => {
return (dispatch, getState): void => {
const currentState = getState().sendForm;
const touched = { ...currentState.touched };
touched.data = true;
const state: State = {
...currentState,
untouched: false,
touched,
data,
};
dispatch({
type: SEND.DATA_CHANGE,
state
});
dispatch( validation() );
}
}
export const onSend = (): any => {
//return onSendERC20();
return async (dispatch, getState): Promise<any> => {
const state: State = getState().sendForm;
const web3instance = getState().web3.filter(w3 => w3.coin === state.coin)[0];
const web3 = web3instance.web3;
const account = getState().accounts.find(a => a.checksum === state.checksum && a.index === state.accountIndex && a.coin === state.coin);
const address_n = account.addressPath;
let data: string = '';
let txAmount = web3.toHex(web3.toWei(state.amount, 'ether'));
let txAddress = state.address;
if (state.coin !== state.token) {
const tokens = getState().tokens
const t = tokens.find(t => t.ethAddress === account.address && t.symbol === state.token);
const contract = web3instance.erc20.at(t.address);
data = contract.transfer.getData(state.address, state.amount, {
from: account.address,
gasLimit: state.gasLimit,
gasPrice: state.gasPrice
});
txAmount = '0x00';
txAddress = t.address;
}
const txData = {
address_n,
to: address,
value: web3.toHex(web3.toWei(amount, 'ether')),
data: '',
chainId: 3
// from: currentAddress.address
to: txAddress,
value: txAmount,
data,
//chainId: 3 // ropsten
chainId: web3instance.chainId,
nonce: web3.toHex(account.nonce),
gasLimit: web3.toHex(state.gasLimit),
gasPrice: web3.toHex( EthereumjsUnits.convert(state.gasPrice, 'gwei', 'wei') ),
r: '',
s: '',
v: ''
}
const nonce = await getNonce(web3, currentAddress.address);
const gasOptions = {
to: txData.to,
data: txData.data
}
const gasLimit = await estimateGas(web3, gasOptions);
const gasPrice = await getGasPrice(web3);
//const nonce = await getNonce(web3, currentAddress.address);
//txData.nonce = web3.toHex(nonce);
// const gasOptions = {
// to: txData.to,
// data: txData.data
// }
// const gasPrice = await getGasPrice(web3);
txData.nonce = web3.toHex(nonce);
txData.gasLimit = web3.toHex(gasLimit);
txData.gasPrice = web3.toHex(gasPrice);
// txData.nonce = web3.toHex(nonce);
// txData.gasLimit = web3.toHex(gasLimit);
// txData.gasPrice = web3.toHex( EthereumjsUnits.convert(gasPrice, 'gwei', 'wei') );
// console.log("---->GASSS", txData, gasLimit, gasPrice, EthereumjsUnits.convert(gasPrice, 'gwei', 'wei'));
const selected = findSelectedDevice(getState().connect);
let signedTransaction = await TrezorConnect.ethereumSignTransaction({
device: {
path: selected.path,
instance: selected.instance,
state: selected.checksum
},
//path: "m/44'/60'/0'/0/0",
address_n: txData.address_n,
nonce: strip(txData.nonce),
@ -82,24 +674,85 @@ export const onSend = (addressId): void => {
gas_limit: strip(txData.gasLimit),
to: strip(txData.to),
value: strip(txData.value),
data: txData.data,
data: strip(txData.data),
chain_id: txData.chainId
});
if (!signedTransaction || !signedTransaction.success) {
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Transaction error',
message: signedTransaction.data.error,
cancelable: true,
actions: [ ]
}
})
return;
}
txData.r = '0x' + signedTransaction.data.r;
txData.s = '0x' + signedTransaction.data.s;
txData.v = web3.toHex(signedTransaction.data.v);
const tx = new EthereumjsTx(txData);
const serializedTx = '0x' + tx.serialize().toString('hex');
const gasLimit2 = await estimateGas(web3, txData);
console.log("---->GASSS", txData, gasLimit2.toString() );
try {
const tx = new EthereumjsTx(txData);
const serializedTx = '0x' + tx.serialize().toString('hex');
const txid = await pushTx(web3, serializedTx);
dispatch({
type: SEND.TX_COMPLETE,
address: account,
txid,
txData,
});
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'success',
title: 'Transaction success',
message: `<a href="https://ropsten.etherscan.io/tx/${txid}">detail</a>`,
cancelable: true,
actions: []
}
});
} catch(error) {
const txid = await push(web3, serializedTx);
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Transaction error',
message: error.message || error,
cancelable: true,
actions: [ ]
}
});
}
// const tx = new EthereumjsTx(txData);
// console.log("2222", tx, tx.toJSON(), tx.from, tx.to);
// const serializedTx = '0x' + tx.serialize().toString('hex');
// console.log("----> PUSZ TX", web3, currentAddress, serializedTx)
// const txid = await pushTx(web3, serializedTx);
// console.log("----> PUSZ TX2", web3, serializedTx)
dispatch({
type: ACTIONS.ON_TX_COMPLETE,
address: currentAddress,
txid,
txData,
})
// dispatch({
// type: SEND.TX_COMPLETE,
// address: currentAddress,
// txid,
// txData,
// })
// const [ url ] = getState().router.location.pathname.split('/send');
// dispatch( push(url) );
}
}

@ -0,0 +1,282 @@
/* @flow */
'use strict';
import EthereumjsUtil from 'ethereumjs-util';
import * as ACTIONS from './index';
import * as SUMMARY from './constants/summary';
import * as TOKEN from './constants/Token';
import * as ADDRESS from './constants/Address';
import { resolveAfter } from '../utils/promiseUtils';
import { getTokenInfoAsync, getTokenBalanceAsync } from './Web3Actions';
import { initialState } from '../reducers/SummaryReducer';
import type { State } from '../reducers/SummaryReducer';
import { findSelectedDevice } from '../reducers/TrezorConnectReducer';
export const init = (): any => {
return (dispatch, getState): void => {
const { location } = getState().router;
const urlParams = location.params;
const selected = findSelectedDevice( getState().connect );
if (!selected) return;
const state: State = {
...initialState,
checksum: selected.checksum,
accountIndex: parseInt(urlParams.address),
coin: urlParams.coin,
location: location.pathname,
};
dispatch({
type: SUMMARY.INIT,
state: state
});
}
}
export const update = (newProps: any): any => {
return (dispatch, getState): void => {
const {
summary,
router
} = getState();
const isLocationChanged: boolean = router.location.pathname !== summary.location;
if (isLocationChanged) {
dispatch( init() );
return;
}
}
}
export const dispose = (address: string): any => {
return {
type: SUMMARY.DISPOSE
}
}
export const onDetailsToggle = (): any => {
return {
type: SUMMARY.DETAILS_TOGGLE
}
}
// export const init = (address: string): any => {
// return (dispatch, getState): void => {
// const { location } = getState().router;
// const urlParams = location.params;
// const selected = findSelectedDevice(getState().connect);
// const accounts = getState().accounts;
// // const currentAccount = accounts.find(a => a.index === parseInt(urlParams.address) && a.coin === urlParams.coin && a.deviceId === urlParams.device && a.loaded);
// const currentAccount = accounts.find(a => a.index === parseInt(urlParams.address) && a.coin === urlParams.coin && a.checksum === selected.checksum);
// if (!currentAccount) {
// console.log("STATER", getState())
// // account not found
// return;
// }
// const web3instance = getState().web3.find(w3 => w3.coin === urlParams.coin);
// if (!web3instance) {
// // no backend for this coin
// return;
// }
// const state: State = {
// ...initialState,
// loaded: true,
// address: currentAccount.address,
// coin: urlParams.coin,
// balance: currentAccount.balance,
// };
// dispatch({
// type: SUMMARY.INIT,
// state
// });
// }
// }
export const loadTokens = (input: string): any => {
return async (dispatch, getState): Promise<any> => {
if (input.length < 1) return null;
const { ethTokens } = getState().localStorage;
const value = input.toLowerCase();
const result = ethTokens.filter(t =>
t.symbol.toLowerCase().indexOf(value) >= 0 ||
t.address.toLowerCase().indexOf(value) >= 0 ||
t.name.toLowerCase().indexOf(value) >= 0
);
//const result = ethTokens.filter(t => t.symbol.toLowerCase().indexOf(lower) >= 0);
console.log("RESULT!", result.length, result)
if (result.length > 0) {
return { options: result };
} else {
const web3instance = getState().web3.find(w3 => w3.coin === 'eth');
const info = await getTokenInfoAsync(web3instance.erc20, input);
info.address = input;
console.log("FETCH", info)
if (info) {
return {
options: [ info ]
}
} else {
return {
}
}
//await resolveAfter(300000);
//await resolveAfter(3000);
}
}
}
export const selectToken = (token: any, account: any): any => {
return async (dispatch, getState): Promise<any> => {
console.warn("ADD", token, account)
const web3instance = getState().web3.find(w3 => w3.coin === account.coin);
dispatch({
type: TOKEN.ADD,
payload: {
...token,
ethAddress: account.address,
checksum: account.checksum
}
});
// TODO: load token balance
const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, token.address, account.address);
dispatch({
type: TOKEN.SET_BALANCE,
payload: {
ethAddress: account.address,
address: token.address,
balance: tokenBalance.toString()
}
})
}
}
export const onTokenSearch = (search: string): any => {
return {
type: ACTIONS.TOKENS_SEARCH,
search
}
}
export const onCustomTokenToggle = (): any => {
return {
type: ACTIONS.TOKENS_CUSTOM_TOGGLE
}
}
export const onCustomTokenAddressChange = (value: string): any => {
// todo:
// -validate addres
// - if adress is ok, try to fetch token info
// - check if addres does not exist in predefined coins
// return {
// type: ACTIONS.TOKENS_CUSTOM_ADDRESS_CHANGE,
// value
// }
return async (dispatch, getState) => {
const valid: boolean = EthereumjsUtil.isValidAddress(value);
if (valid) {
dispatch({
type: ACTIONS.TOKENS_CUSTOM_ADDRESS_CHANGE,
value,
valid,
fetching: true
});
const { web3, abi } = getState().web3;
const contract = web3.eth.contract(abi).at(value);
contract.name.call((error, name) => {
if (error) {
// TODO: skip
}
contract.symbol.call((error, symbol) => {
if (error) {
// TODO: skip
}
contract.decimals.call((error, decimals) => {
console.log("fetched!", name, symbol, decimals)
})
});
})
} else {
dispatch({
type: ACTIONS.TOKENS_CUSTOM_ADDRESS_CHANGE,
value,
valid
});
}
console.log("VALID!!!", valid);
}
}
export const onCustomTokenNameChange = (value: string): any => {
return {
type: ACTIONS.TOKENS_CUSTOM_NAME_CHANGE,
value
}
}
export const onCustomTokenShortcutChange = (value: string): any => {
return {
type: ACTIONS.TOKENS_CUSTOM_SHORTCUT_CHANGE,
value
}
}
export const onCustomTokenDecimalChange = (value: string): any => {
return {
type: ACTIONS.TOKENS_CUSTOM_DECIMAL_CHANGE,
value
}
}
export const onCustomTokenAdd = (): any => {
return {
type: ACTIONS.TOKENS_CUSTOM_ADD
}
}

@ -1,260 +0,0 @@
/* @flow */
'use strict';
import TrezorConnect, { UI } from 'trezor-connect';
import * as ACTIONS from './index';
//import wallet from 'ethereumjs-wallet';
//import hdkey from 'ethereumjs-wallet/hdkey';
import HDKey from 'hdkey';
import ethUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx';
import * as ethereumUtils from '../utils/ethUtils';
import { hexToString, stringToHex } from '../utils/formatUtils';
import * as Web3Actions from './Web3Actions';
export function onSelectDevice2(path: string): any {
return {
type: ACTIONS.ON_SELECT_DEVICE,
path
}
}
export function discover(txData): any {
return async function (dispatch) {
let response = await TrezorConnect.getPublicKey({ path: "m/44'/60'/0'/0", confirmation: false });
dispatch({
type: 'create_account',
xpub: response.data.xpub,
publicKey: response.data.publicKey,
chainCode: response.data.chainCode,
path: response.data.path
})
}
}
export function signTx(txData): any {
return async function (dispatch) {
console.log("RESP2", txData)
//txData.nonce = "0x01";
//txData.gasPrice = "0x02540be400";
// txData.gasLimit = "0x5208";
// txData.value = "0x5af3107a4000";
let response = await TrezorConnect.ethereumSignTransaction({
//path: "m/44'/60'/0'/0/0",
address_n: txData.address_n,
nonce: ethereumUtils.strip(txData.nonce),
gas_price: ethereumUtils.strip(txData.gasPrice),
gas_limit: ethereumUtils.strip(txData.gasLimit),
to: ethereumUtils.strip(txData.to),
value: ethereumUtils.strip(txData.value),
data: txData.data,
chain_id: txData.chainId
});
txData.r = '0x' + response.data.r;
txData.s = '0x' + response.data.s;
txData.v = web3.toHex(response.data.v);
console.log("RESP2", response, txData)
const tx = new EthereumjsTx(txData);
var signedTx = '0x' + tx.serialize().toString('hex');
const rawTx2 = {
"nonce":"0x01",
"gasPrice":"0x02540be400",
"gasLimit":"0x5208",
"to":"0x7314e0f1C0e28474bDb6be3E2c3E0453255188f8",
"value":"0x5af3107a4000",
"data":"",
"chainId":3,
"v":"0x2a",
"r":"0x210af4e1698f0437125424ac378da7304dea94dde34cbb57b62624069ceae969",
"s":"0x74c885c3d32330d63e32dff3b79a302ae5ed9b9abcf6e903fd32cbb91d94b6df"
}
var tx2 = new EthereumjsTx(rawTx2);
var signedTx2 = '0x' + tx2.serialize().toString('hex');
console.log(signedTx)
console.log(signedTx2)
console.log(signedTx === signedTx2)
console.log(txData, rawTx2)
// web3.eth.sendRawTransaction(signedTx, function(a1, a2){
// console.log("SIGNEEED", a1, a2)
// })
}
}
export function onSelectDevice(): any {
return async function (dispatch, getState) {
dispatch(Web3Actions.composeTransaction());
}
}
export function onSelectDeviceWeb3(): any {
return async function (dispatch, getState) {
const { web3 } = getState().web3;
console.log("WEB3333", web3)
let resp = await TrezorConnect.getPublicKey({ path: "m/44'/60'/0'/0", confirmation: false });
let hdk = new HDKey();
hdk.publicKey = new Buffer(resp.data.publicKey, 'hex');
hdk.chainCode = new Buffer(resp.data.chainCode, 'hex');
var derivedKey = hdk.derive("m/0");
var address = ethUtil.publicToAddress(derivedKey.publicKey, true);
// balance 0.100100000000000000 eth
var txData = {
address_n: [
(44 | 0x80000000) >>> 0,
(60 | 0x80000000) >>> 0,
(0 | 0x80000000) >>> 0,
0, 0
],
to: '0x7314e0f1C0e28474bDb6be3E2c3E0453255188f8',
value: web3.toHex(web3.toWei('0.0001', 'ether')),
data: '',
chainId: 3
}
web3.eth.getTransactionCount('0x' + address.toString('hex'), (error, result) => {
txData.nonce = web3.toHex(result);
const gasOptions = {
to: txData.to,
data: txData.data
}
web3.eth.estimateGas(gasOptions, (error, result) => {
txData.gasLimit = web3.toHex(result);
web3.eth.getGasPrice(function(error, result){
if (error) throw error;
txData.gasPrice = web3.toHex(result);
console.warn("gesgas", error, result.toString(10), txData)
dispatch( signTx(txData) );
});
});
});
// web3.eth.getTransactionCount('0x' + address.toString('hex'), (error, result) => {
// if (error) throw error;
// console.log("getTransactionCount", result)
// });
// let gas = {
// to: "0x7314e0f1C0e28474bDb6be3E2c3E0453255188f8",
// data: ""
// };
// web3.eth.estimateGas(gas, function(error, result){
// console.warn("estimategas", error, result, result.toString(10))
// web3.eth.getGasPrice(function(error, result){
// console.warn("gesgas", error, result.toString(10) result.)
// })
// });
//console.log("SIGNEEED", signedTx);
//console.log("HDK", derivedKey, address.toString('hex'), rawTx, txData, hexToString(txData.value))
//console.log("HDK", derivedKey, hexToString(txData.value), txData )
// const hd = hdkey.fromExtendedKey(resp.data.xpub);
// console.log("HDKI!", hd)
// var balance = web3.eth.getBalance('0x' + address.toString('hex'), function(error, result){
// if(!error)
// console.log("res", result, result.toString(10) )
// else
// console.error("erro", error);
// });
// web3.eth.sendRawTransaction(signedTx, function(a1, a2){
// console.log("SIGNEEED", a1, a2)
// })
//console.log(web3.eth)
//web3.eth.sendRawTransaction(signedTx).on('receipt', console.log);
// web3.eth.getTransactionCount('0x' + address.toString('hex'), function(error, result) {
// //web3.eth.getTransactionCount("0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", function(error, result) {
// console.log("getTransactionCount", error, result)
// })
// web3.eth.getAccounts(console.log)
// const nonce = '03'; // note - it is hex, not number!!!
// const gas_price = '098bca5a00';
// const gas_limit = 'a43f';
// const to = 'e0b7927c4af23765cb51314a0e0521a9645f0e2a';
// // var value = '01'; // in hexadecimal, in wei - this is 1 wei
// const value = '010000000000000000'; // in hexadecimal, in wei - this is about 18 ETC
// const data = 'a9059cbb000000000000000000000000dc7359317ef4cc723a3980213a013c0433a338910000000000000000000000000000000000000000000000000000000001312d00'; // some contract data
// // var data = null // for no data
// const chain_id = 1; // 1 for ETH, 61 for ETC
// let resp2 = await TrezorConnect.ethereumSignTransaction({
// address_n,
// nonce,
// gas_price,
// gas_limit,
// to,
// value,
// data,
// chain_id
// });
// old fallback
// this.signEthereumTx = function (
// address_n,
// nonce,
// gas_price,
// gas_limit,
// to,
// value,
// data,
// chain_id,
// callback,
// requiredFirmware
// )
// var coinbase = web3.eth.coinbase;
// var balance = web3.eth.getBalance(coinbase);
// console.log(balance.toString(10));
// dispatch({
// type: 'DDD'
// });
}
}

@ -1,95 +1,710 @@
/* @flow */
'use strict';
import TrezorConnect, { UI } from 'trezor-connect';
import TrezorConnect, { UI, DEVICE, DEVICE_EVENT, UI_EVENT } from 'trezor-connect';
import * as ACTIONS from './index';
import * as ADDRESS from './constants/Address';
import * as TOKEN from './constants/Token';
import * as CONNECT from './constants/TrezorConnect';
import * as DISCOVERY from './constants/Discovery';
import * as NOTIFICATION from './constants/notification';
//import wallet from 'ethereumjs-wallet';
//import hdkey from 'ethereumjs-wallet/hdkey';
import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx';
import { Address } from '../reducers/AddressesReducer';
import { getBalance } from '../services/Web3Service';
//import { getBalance } from '../services/Web3Service';
import { getBalance } from './Web3Actions';
import { getTransactionHistory } from '../services/EtherscanService';
import { push } from 'react-router-redux';
export function onSelectDevice2(path: string): any {
return {
type: ACTIONS.ON_SELECT_DEVICE,
path
import { init as initWeb3, getNonce, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions';
import type { Discovery } from '../reducers/DiscoveryReducer';
import { resolveAfter } from '../utils/promiseUtils';
import { getAccounts } from '../utils/reducerUtils';
import { findSelectedDevice, isSavedDevice } from '../reducers/TrezorConnectReducer';
export const init = (): any => {
return async (dispatch, getState): Promise<void> => {
// set listeners
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
dispatch({
type: event.type,
device: event.data
});
});
const version: Object = TrezorConnect.getVersion();
if (version.type === 'library') {
// handle UI events only if TrezorConnect isn't using popup
TrezorConnect.on(UI_EVENT, (type: string, data: any): void => {
// post event to reducers
dispatch({
type,
data
});
});
}
try {
await TrezorConnect.init({
transport_reconnect: true,
});
setTimeout(() => {
dispatch( initWeb3() );
}, 2000)
} catch (error) {
dispatch({
type: CONNECT.INITIALIZATION_ERROR,
error
})
}
}
}
export function remove(devicePath): any {
// called after backend was initialized
// set listeners for connect/disconnect
export const postInit = (): any => {
return (dispatch, getState): void => {
const handleDeviceConnect = (device) => {
dispatch( initConnectedDevice(device) );
}
// const handleDeviceDisconnect = (device) => {
// // remove addresses and discovery from state
// // dispatch( remove(device) );
// }
TrezorConnect.on(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
// TrezorConnect.on(DEVICE.DISCONNECT, handleDeviceDisconnect);
// TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceDisconnect);
// possible race condition:
// devices were connected before Web3 initialized. force DEVICE.CONNECT event on them
const { devices } = getState().connect;
if (devices.length > 0) {
const unacquired = devices.find(d => d.unacquired);
if (unacquired) {
handleDeviceConnect(unacquired);
} else {
const latest = devices.sort((a, b) => {
if (!a.ts || !b.ts) {
return -1;
} else {
return a.ts > b.ts ? 1 : -1;
}
});
console.log("LATEST", latest)
}
}
for (let d of devices) {
handleDeviceConnect(d);
}
}
}
export const initConnectedDevice = (device: any): any => {
return (dispatch, getState): void => {
const selected = findSelectedDevice(getState().connect);
if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
dispatch( onSelectDevice(device) );
} else if (!selected) {
dispatch( onSelectDevice(device) );
}
}
}
// selection from Aside dropdown
// or after acquiring device
export function onSelectDevice(device: any): any {
return (dispatch, getState): void => {
// || device.isUsedElsewhere
if (device.unacquired) {
dispatch( push(`/device/${ device.path }/acquire`) );
} else if (device.features.bootloader_mode) {
dispatch( push(`/device/${ device.path }/bootloader`) );
} else if (device.instance) {
dispatch( push(`/device/${ device.features.device_id }:${ device.instance }`) );
} else {
//dispatch( push(`/device/${ device.features.device_id }/coin/etc/address/0`) );
dispatch( push(`/device/${ device.features.device_id }`) );
}
}
}
// TODO: as TrezorConnect method
const __getDeviceState = async (path, instance): Promise<any> => {
return await TrezorConnect.getPublicKey({
device: {
path,
instance
},
// selectedDevice: path,
instance: instance,
path: "m/1'/0'/0'",
confirmation: false
});
}
export const switchToFirstAvailableDevice = (): any => {
return async (dispatch, getState): Promise<void> => {
const { devices } = getState().connect;
if (devices.length > 0) {
// TODO: Priority:
// 1. Unacquired
// 2. First connected
// 3. Saved with latest timestamp
// 4. First from the list
const unacquired = devices.find(d => d.unacquired);
if (unacquired) {
dispatch( initConnectedDevice(unacquired) );
} else {
const latest = devices.sort((a, b) => {
if (!a.ts || !b.ts) {
return -1;
} else {
return a.ts > b.ts ? 1 : -1;
}
});
dispatch( initConnectedDevice(devices[0]) );
}
} else {
dispatch( push('/') );
dispatch({
type: CONNECT.SELECT_DEVICE,
payload: null
})
}
}
}
export const getSelectedDeviceState = (): any => {
return async (dispatch, getState): Promise<void> => {
const selected = findSelectedDevice(getState().connect);
console.warn("init selected", selected)
if (selected
&& selected.connected
&& !selected.unacquired
&& !selected.acquiring
&& !selected.checksum) {
const response = await __getDeviceState(selected.path, selected.instance);
if (response && response.success) {
dispatch({
type: CONNECT.AUTH_DEVICE,
device: selected,
checksum: response.data.xpub
});
} else {
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Authentification error',
message: response.data.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch( getSelectedDeviceState() );
}
}
]
}
})
}
}
}
}
export const deviceDisconnect = (device: any): any => {
return async (dispatch, getState): Promise<void> => {
if (!device || !device.features) return null;
const selected = findSelectedDevice(getState().connect);
if (selected && selected.features.device_id === device.features.device_id) {
stopDiscoveryProcess(selected);
}
const affected = getState().connect.devices.filter(d => d.features && d.checksum && !d.remember && d.features.device_id === device.features.device_id);
if (affected.length > 0) {
dispatch({
type: CONNECT.REMEMBER_REQUEST,
device,
allInstances: affected
});
}
// if (selected && selected.checksum) {
// if (device.features && device.features.device_id === selected.features.device_id) {
// stopDiscoveryProcess(selected);
// }
// }
// // stop running discovery process on this device
// if (selected && selected.path === device.path){
// if (selected.checksum) {
// stopDiscoveryProcess(selected);
// }
// }
// // check if disconnected device was remembered before.
// // request modal if not
// const affected = getState().connect.devices.filter(d => d.path === device.path && d.checksum && !device.remember);
// check if reload is needed
if (!selected) {
dispatch( switchToFirstAvailableDevice() );
}
}
}
export const coinChanged = (coin: ?string): any => {
return (dispatch, getState): void => {
const selected = findSelectedDevice(getState().connect);
dispatch( stopDiscoveryProcess(selected) );
if (coin) {
dispatch( startDiscoveryProcess(selected, coin) );
}
}
}
export function acquire(): any {
return async (dispatch, getState) => {
const { addresses } = getState().addresses;
const availableAddresses = addresses.filter(a => a.devicePath !== devicePath);
const selected = findSelectedDevice(getState().connect);
const saved = getState().connect.devices.map(d => {
if (d.checksum) {
return {
instance: d.instance,
checksum: d.checksum
}
} else {
return null;
}
});
//const response = await __acquire(selected.path, selected.instance);
dispatch({
type: ACTIONS.ADDRESS_DELETE,
addresses: availableAddresses
type: CONNECT.START_ACQUIRING,
device: selected
});
const response = await TrezorConnect.getFeatures({
device: {
path: selected.path,
}
});
const selected2 = findSelectedDevice(getState().connect);
dispatch({
type: CONNECT.STOP_ACQUIRING,
device: selected2
});
if (response && response.success) {
dispatch({
type: DEVICE.ACQUIRED,
// checksum: response
})
} else {
// TODO: handle invalid pin?
console.log("-errror ack", response)
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Acquire device error',
message: response.data.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(acquire())
}
}
]
}
})
}
}
}
export const forgetDevice = (device: any) => {
return (dispatch: any, getState: any): any => {
// find accounts associated with this device
const accounts: Array<any> = getState().accounts.find(a => a.checksum === device.checksum);
// find discovery processes associated with this device
const discovery: Array<any> = getState().discovery.find(d => d.checksum === device.checksum);
}
}
// called from Aside - device menu (forget single instance)
export const forget = (device: any) => {
return {
type: CONNECT.FORGET_REQUEST,
device
};
}
export const duplicateDevice = (device: any) => {
return async (dispatch: any, getState: any): Promise<void> => {
dispatch({
type: CONNECT.TRY_TO_DUPLICATE,
device
})
}
}
export function discover(devicePath): any {
export const onDuplicateDevice = () => {
return async (dispatch: any, getState: any): Promise<void> => {
const selected = findSelectedDevice(getState().connect);
dispatch(onSelectDevice(selected));
}
}
export const beginDiscoveryProcess = (device: any, coin: string): any => {
return async (dispatch, getState) => {
const { web3 } = getState().web3;
const { config } = getState().localStorage;
const coinToDiscover = config.coins.find(c => c.symbol === coin);
// TODO: validate device checksum
// const checksum = await __acquire(device.path, device.instance);
// if (checksum && checksum.success) {
// if (checksum.data.xpub !== device.checksum) {
// console.error("Incorrect checksum!");
// return;
// }
// }
// acquire and hold session
// get xpub from TREZOR
const response = await TrezorConnect.getPublicKey({
device: {
path: device.path,
instance: device.instance,
state: device.checksum
},
path: coinToDiscover.bip44,
confirmation: false,
keepSession: true
});
if (!response.success) {
// TODO: check message
console.warn("DISCO ERROR", response)
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Discovery error',
message: response.data.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(startDiscoveryProcess(device, coin))
}
}
]
}
})
return;
}
const response = await TrezorConnect.getPublicKey({ path: "m/44'/60'/0'/0", confirmation: false });
// TODO: check for interruption
// TODO: handle response error
const basePath: Array<number> = response.data.path;
const hdKey = new HDKey();
hdKey.publicKey = new Buffer(response.data.publicKey, 'hex');
hdKey.chainCode = new Buffer(response.data.chainCode, 'hex');
const loop = async (index: number) => {
const derivedKey = hdKey.derive(`m/${index}`);
const path = basePath.concat(index);
const ethAddress: string = '0x' + EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex');
const address = new Address(devicePath, index, path, ethAddress);
// send data to reducer
dispatch({
type: DISCOVERY.START,
coin: coinToDiscover.shortcut,
device,
xpub: response.data.publicKey,
basePath,
hdKey,
});
dispatch( startDiscoveryProcess(device, coin) );
}
}
export const discoverAddress = (device: any, discoveryProcess: Discovery): any => {
return async (dispatch, getState) => {
const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`);
const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex);
const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex');
const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress);
const coin = discoveryProcess.coin;
dispatch({
type: ADDRESS.CREATE,
device,
coin,
index: discoveryProcess.accountIndex,
path,
address: ethAddress
});
// TODO: check if address was created before
// verify address with TREZOR
const verifyAddress = await TrezorConnect.ethereumGetAddress({
device: {
path: device.path,
instance: device.instance,
state: device.checksum
},
address_n: path,
showOnTrezor: false
});
if (discoveryProcess.interrupted) return;
if (verifyAddress && verifyAddress.success) {
//const trezorAddress: string = '0x' + verifyAddress.data.message.address;
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.data.message.address);
if (trezorAddress !== ethAddress) {
// throw inconsistent state error
console.warn("Inconsistent state", trezorAddress, ethAddress);
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Address validation error',
message: `Addresses are different. ${ trezorAddress } : ${ ethAddress }`,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(startDiscoveryProcess(device, discoveryProcess.coin))
}
}
]
}
});
return;
}
} else {
// handle TREZOR communication error
dispatch({
type: ACTIONS.ADDRESS_CREATE,
devicePath,
address
})
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Address validation error',
message: verifyAddress.data.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(startDiscoveryProcess(device, discoveryProcess.coin))
}
}
]
}
});
return;
}
const balance = await getBalance(ethAddress);
const web3instance = getState().web3.find(w3 => w3.coin === coin);
const balance = await getBalanceAsync(web3instance.web3, ethAddress);
if (discoveryProcess.interrupted) return;
dispatch({
type: ADDRESS.SET_BALANCE,
address: ethAddress,
balance: web3instance.web3.fromWei(balance.toString(), 'ether')
});
const userTokens = [];
// const userTokens = [
// { symbol: 'T01', address: '0x58cda554935e4a1f2acbe15f8757400af275e084' },
// { symbol: 'Lahod', address: '0x3360d0ee34a49d9ac34dce88b000a2903f2806ee' },
// ];
for (let i = 0; i < userTokens.length; i++) {
const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, userTokens[i].address, ethAddress);
if (discoveryProcess.interrupted) return;
dispatch({
type: ACTIONS.ADDRESS_SET_BALANCE,
address,
balance: web3.fromWei(balance.toString(), 'ether')
type: TOKEN.SET_BALANCE,
tokenName: userTokens[i].symbol,
ethAddress: ethAddress,
tokenAddress: userTokens[i].address,
balance: tokenBalance.toString()
})
}
const nonce = await getNonce(web3instance.web3, ethAddress);
if (discoveryProcess.interrupted) return;
dispatch({
type: ADDRESS.SET_NONCE,
address: ethAddress,
nonce: nonce
});
const addressIsEmpty = nonce < 1 && !balance.greaterThan(0);
if (!addressIsEmpty) {
//dispatch( startDiscoveryProcess(device, discoveryProcess.coin) );
dispatch( discoverAddress(device, discoveryProcess) );
} else {
// release acquired sesssion
await TrezorConnect.getPublicKey({
device: {
path: device.path,
instance: device.instance,
state: device.checksum
},
path: "m/44'/60'/0'/0",
confirmation: false,
keepSession: false
});
if (discoveryProcess.interrupted) return;
dispatch({
type: DISCOVERY.COMPLETE,
device,
coin
});
}
}
}
export function startDiscoveryProcess(device: any, coin: string, ignoreCompleted?: boolean): any {
return (dispatch, getState) => {
const selected = findSelectedDevice(getState().connect);
if (!selected) {
// TODO: throw error
console.error("Start discovery: no selected device", device)
return;
} else if (selected.path !== device.path) {
console.error("Start discovery: requested device is not selected", device, selected)
return;
} else if (!selected.checksum) {
console.warn("Start discovery: Selected device wasn't authenticated yet...")
return;
}
// const history = await getTransactionHistory(ethAddress);
// dispatch({
// type: ACTIONS.ADDRESS_SET_HISTORY,
// address,
// history
// })
const discovery = getState().discovery;
let discoveryProcess: ?Discovery = discovery.find(d => d.checksum === device.checksum && d.coin === coin);
// TODO redirect to 1st account
if (index === 0) {
dispatch( push('/address/0') );
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
dispatch({
type: DISCOVERY.WAITING,
device,
coin
});
return;
}
if (!discoveryProcess) {
dispatch( beginDiscoveryProcess(device, coin) );
return;
} else {
if (discoveryProcess.completed && !ignoreCompleted) {
dispatch({
type: DISCOVERY.COMPLETE,
device,
coin
});
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
// discovery cycle was interrupted
// start from beginning
dispatch( beginDiscoveryProcess(device, coin) );
} else {
dispatch( discoverAddress(device, discoveryProcess) );
}
}
}
}
export const restoreDiscovery = (): any => {
return (dispatch, getState): void => {
const selected = findSelectedDevice(getState().connect);
if (selected && selected.connected && !selected.unacquired) {
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.checksum === selected.checksum && d.waitingForDevice);
if (discoveryProcess) {
dispatch( startDiscoveryProcess(selected, discoveryProcess.coin) );
}
}
}
}
// there is no discovery process but it should be
// this is possible race condition when coin was changed in url but device wasn't authenticated yet
// try to discovery after CONNECT.AUTH_DEVICE action
export const checkDiscoveryStatus = (): any => {
return (dispatch, getState): void => {
const selected = findSelectedDevice(getState().connect);
if (!selected) return;
if (index < 2) {
loop( index + 1);
const urlParams = getState().router.location.params;
if (urlParams.coin) {
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.checksum === selected.checksum && d.coin === urlParams.coin);
if (!discoveryProcess) {
dispatch( startDiscoveryProcess(selected, urlParams.coin) );
}
}
}
}
loop(0);
export function stopDiscoveryProcess(device: any): any {
// TODO: release devices session
// corner case swtich /eth to /etc (discovery start stop - should not be async)
return {
type: DISCOVERY.STOP,
device
}
}
export function onSelectDevice(): any {
return async (dispatch, getState) => {
// dispatch(Web3Actions.composeTransaction());
export function addAddress(): any {
return (dispatch, getState) => {
const selected = findSelectedDevice(getState().connect);
dispatch( startDiscoveryProcess(selected, getState().router.location.params.coin, true) ); // TODO: coin nicer
}
}

@ -1,154 +0,0 @@
/* @flow */
'use strict';
import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx';
import TrezorConnect from 'trezor-connect';
import { strip } from '../utils/ethUtils';
export function getTransaction(web3, txid) {
return new Promise((resolve, reject) => {
web3.eth.getTransaction(txid, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
export function getBalance(web3, address) {
return new Promise((resolve, reject) => {
web3.eth.getBalance(address, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
export function getNonce(web3, address) {
return new Promise((resolve, reject) => {
web3.eth.getTransactionCount(address, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
export function estimateGas(web3, gasOptions) {
return new Promise((resolve, reject) => {
web3.eth.estimateGas(gasOptions, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
})
}
export function getGasPrice(web3) {
return new Promise((resolve, reject) => {
web3.eth.getGasPrice((error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
})
}
export function push(web3, tx) {
return new Promise((resolve, reject) => {
web3.eth.sendRawTransaction(tx, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
})
}
export function composeTransaction() {
return async function (dispatch, getState) {
const { web3 } = getState().web3;
const { address, amount } = getState().sendForm;
const resp = await TrezorConnect.getPublicKey({ path: "m/44'/60'/0'/0", confirmation: false });
const hdk = new HDKey();
hdk.publicKey = new Buffer(resp.data.publicKey, 'hex');
hdk.chainCode = new Buffer(resp.data.chainCode, 'hex');
const derivedKey = hdk.derive("m/0");
const myAddress = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true);
const txData = {
address_n: [
(44 | 0x80000000) >>> 0,
(60 | 0x80000000) >>> 0,
(0 | 0x80000000) >>> 0,
0, 0
],
to: address,
value: web3.toHex(web3.toWei(amount, 'ether')),
data,
chainId: 3
}
console.log("NONCE", myAddress)
const nonce = await getNonce(web3, '0x' + myAddress.toString('hex') );
console.log("NONCE", nonce)
const gasOptions = {
to: txData.to,
data: txData.data
}
const gasLimit = await estimateGas(web3, gasOptions);
const gasPrice = await getGasPrice(web3);
txData.nonce = web3.toHex(nonce);
txData.gasLimit = web3.toHex(gasLimit);
txData.gasPrice = web3.toHex(gasPrice);
console.log("NONCE", nonce, gasLimit, gasPrice)
let signedTransaction = await TrezorConnect.ethereumSignTransaction({
//path: "m/44'/60'/0'/0/0",
address_n: txData.address_n,
nonce: strip(txData.nonce),
gas_price: strip(txData.gasPrice),
gas_limit: strip(txData.gasLimit),
to: strip(txData.to),
value: strip(txData.value),
data: txData.data,
chain_id: txData.chainId
});
txData.r = '0x' + signedTransaction.data.r;
txData.s = '0x' + signedTransaction.data.s;
txData.v = web3.toHex(signedTransaction.data.v);
const tx = new EthereumjsTx(txData);
const serializedTx = '0x' + tx.serialize().toString('hex');
const txid = await push(web3, serializedTx);
dispatch({
type: 'tx_complete',
txid
})
console.log("TXID", txid);
}
}

@ -1,37 +1,270 @@
/* @flow */
'use strict';
import Web3 from 'web3';
import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx';
import TrezorConnect from 'trezor-connect';
import { strip } from '../utils/ethUtils';
import * as ACTIONS from './index';
import * as ADDRESS from './constants/Address';
import * as WEB3 from './constants/Web3';
import { loadHistory } from '../services/EtherscanService';
import { httpRequest } from '../utils/networkUtils';
type ActionMethod = (dispatch: any, getState: any) => Promise<any>;
export function getBalance(address) {
export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod {
return async (dispatch, getState) => {
const { web } = getState().web3;
web3.eth.getBalance(address.address, (error, balance) => {
if (!error) {
const { config, ethERC20 } = getState().localStorage;
const coin = config.coins[ coinIndex ];
if (!coin) {
// all instances done
dispatch({
type: WEB3.READY,
});
return;
}
const coinName = coin.shortcut;
const urls = coin.backends[0].urls;
let web3host: string = urls[0];
if (web3) {
const currentHost = web3.currentProvider.host;
let currentHostIndex: number = urls.indexOf(currentHost);
if (currentHostIndex + 1 < urls.length) {
web3host = urls[currentHostIndex + 1];
} else {
console.error("TODO: Backend " + coinName + " not working");
// try next coin
dispatch( init(web3, coinIndex + 1) );
return;
}
}
//const instance = new Web3(window.web3.currentProvider);
const instance = new Web3( new Web3.providers.HttpProvider(web3host) );
// instance = new Web3( new Web3.providers.HttpProvider('https://pyrus2.ubiqscan.io') ); // UBQ
//instance = new Web3( new Web3.providers.HttpProvider('https://node.expanse.tech/') ); // EXP
//instance = new Web3( new Web3.providers.HttpProvider('http://10.34.0.91:8545/') );
//web3 = new Web3(new Web3.providers.HttpProvider("https://api.myetherapi.com/rop"));
//instance = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io2/QGyVKozSUEh2YhL4s2G4"));
//web3 = new Web3( new Web3.providers.HttpProvider("ws://34.230.234.51:30303") );
// initial check if backend is running
// instance.version.getNetwork(function(error, chainId){
// if (!error) {
instance.eth.getGasPrice((error, gasPrice) => {
if (error) {
// try different url
dispatch( init(instance, coinIndex) );
} else {
const erc20 = instance.eth.contract(ethERC20);
dispatch({
type: ACTIONS.ADDRESS_SET_BALANCE,
address,
balance: web3.fromWei(balance.toString(), 'ether')
})
type: WEB3.CREATE,
name: coinName,
web3: instance,
erc20,
chainId: instance.version.network
});
dispatch({
type: WEB3.GAS_PRICE_UPDATED,
coin: coinName,
gasPrice
});
// console.log("GET CHAIN", instance.version.network)
// instance.version.getWhisper((err, shh) => {
// console.log("-----whisperrr", error, shh)
// })
// const sshFilter = instance.ssh.filter('latest');
// sshFilter.watch((error, blockHash) => {
// console.warn("SSH", error, blockHash);
// });
//const shh = instance.shh.newIdentity();
const latestBlockFilter = instance.eth.filter('latest');
latestBlockFilter.watch(async (error, blockHash) => {
if (error) {
console.warn("ERROR!", error);
// setInterval(() => {
// dispatch( getGasPrice(coinName) );
// }, 5000);
}
dispatch({
type: WEB3.BLOCK_UPDATED,
name: coinName,
blockHash
});
// TODO: filter only current device
const accounts = getState().accounts.filter(a => a.coin === coinName);
for (const addr of accounts) {
dispatch( getBalance(addr) );
}
dispatch( getGasPrice(coinName) );
// if (pendingTxs.length > 0) {
// for (const tx of pendingTxs) {
// dispatch( getTransactionReceipt(tx) );
// }
// }
});
// init next coin
dispatch( init(instance, coinIndex + 1) );
}
});
// let instance2 = new Web3( new Web3.providers.HttpProvider('https://pyrus2.ubiqscan.io') );
// console.log("INIT WEB3", instance, instance2);
// instance2.eth.getGasPrice((error, gasPrice) => {
// console.log("---gasss price from UBQ", gasPrice)
// });
}
}
function initBlockTicker() {
}
export function initContracts(): ActionMethod {
return async (dispatch, getState) => {
const { web3, abi, tokens } = getState().web3;
const contracts = [];
for (let token of tokens) {
contracts.push({
contract: web3.eth.contract(abi).at(token.address),
name: token.name,
symbol: token.symbol,
decimal: token.decimal
});
// web3.eth.contract(abi).at(token.address).balanceOf('0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad', (e, r) => {
// console.warn('contrR', e, r.toString(10));
// });
}
const contract = web3.eth.contract(abi).at('0x58cda554935e4a1f2acbe15f8757400af275e084');
contract.name.call((error, name) => {
if (error) {
// TODO: skip
}
contract.symbol.call((error, symbol) => {
if (error) {
// TODO: skip
}
contract.decimals.call((error, decimals) => {
console.log("nameeeee", name, symbol, decimals)
})
});
})
}
}
export function getGasPrice(coinName: string): ActionMethod {
return async (dispatch, getState) => {
const index: number = getState().web3.findIndex(w3 => {
return w3.coin === coinName;
});
const web3 = getState().web3[ index ].web3;
web3.eth.getGasPrice((error, gasPrice) => {
if (!error) {
dispatch({
type: WEB3.GAS_PRICE_UPDATED,
coin: coinName,
gasPrice
});
}
});
}
}
export function getBalance(addr: Address): ActionMethod {
return async (dispatch, getState) => {
const web3instance = getState().web3.filter(w3 => w3.coin === addr.coin)[0];
const web3 = web3instance.web3;
web3.eth.getBalance(addr.address, (error, balance) => {
if (!error) {
const newBalance: string = web3.fromWei(balance.toString(), 'ether');
if (addr.balance !== newBalance) {
dispatch({
type: ADDRESS.SET_BALANCE,
address: addr.address,
balance: newBalance
});
// dispatch( loadHistory(addr) );
}
}
});
}
}
export function getTransactionReceipt(txid: string): any {
return async (dispatch, getState) => {
const { web3 } = getState().web3;
//web3.eth.getTransactionReceipt(txid, (error, tx) => {
web3.eth.getTransaction(txid, (error, tx) => {
if (tx && tx.blockNumber) {
web3.eth.getBlock(tx.blockHash, (error, block) => {
console.log("---MAMM BLOCK", error, block, tx, tx.blockHash)
dispatch({
type: ACTIONS.TX_CONFIRMED,
txid,
tx,
block
})
});
}
});
}
}
export function updateLastBlock(hash: string) {
return {
type: 'web3__update_last_block',
hash
}
}
export function getTransaction(web3, txid) {
return new Promise((resolve, reject) => {
@ -45,7 +278,9 @@ export function getTransaction(web3, txid) {
});
}
export function getBalance2(web3, address) {
export function getBalanceAsync(web3, address) {
return new Promise((resolve, reject) => {
web3.eth.getBalance(address, (error, result) => {
if (error) {
@ -57,6 +292,20 @@ export function getBalance2(web3, address) {
});
}
export const getTokenBalanceAsync = (erc20: any, token: any, address: any): Promise<any> => {
return new Promise((resolve, reject) => {
const contr = erc20.at(token);
contr.balanceOf(address, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
export function getNonce(web3, address) {
return new Promise((resolve, reject) => {
web3.eth.getTransactionCount(address, (error, result) => {
@ -69,6 +318,41 @@ export function getNonce(web3, address) {
});
}
export function getTokenInfoAsync(erc20: any, address: string): Promise<any> {
return new Promise((resolve, reject) => {
const contract = erc20.at(address);
const info = {};
// TODO: handle errors
contract.name.call((e, name) => {
if (e) {
//console.log("1", address, e)
//resolve(null);
//return;
}
info.name = name;
contract.symbol.call((e, symbol) => {
if (e) {
console.log("2", e)
resolve(null);
return;
}
info.symbol = symbol;
contract.decimals.call((e, decimals) => {
if (e) {
console.log("3", e)
resolve(null);
return;
}
info.decimals = decimals.toString();
resolve(info);
});
})
});
});
}
export function estimateGas(web3, gasOptions) {
return new Promise((resolve, reject) => {
web3.eth.estimateGas(gasOptions, (error, result) => {
@ -81,7 +365,7 @@ export function estimateGas(web3, gasOptions) {
})
}
export function getGasPrice(web3) {
export function getGasPrice2(web3) {
return new Promise((resolve, reject) => {
web3.eth.getGasPrice((error, result) => {
if (error) {
@ -93,7 +377,7 @@ export function getGasPrice(web3) {
})
}
export function push(web3, tx) {
export function pushTx(web3, tx) {
return new Promise((resolve, reject) => {
web3.eth.sendRawTransaction(tx, (error, result) => {
if (error) {
@ -168,7 +452,7 @@ export function composeTransaction() {
const tx = new EthereumjsTx(txData);
const serializedTx = '0x' + tx.serialize().toString('hex');
const txid = await push(web3, serializedTx);
const txid = await pushTx(web3, serializedTx);
dispatch({
type: 'tx_complete',
@ -177,4 +461,9 @@ export function composeTransaction() {
console.log("TXID", txid);
}
}
}

@ -0,0 +1,8 @@
/* @flow */
'use strict';
export const START: string = 'discovery__start';
export const STOP: string = 'discovery__stop';
export const COMPLETE: string = 'discovery__complete';
export const WAITING: string = 'discovery__waiting';
export const FROM_STORAGE: string = 'discovery__from_storage';

@ -0,0 +1,6 @@
/* @flow */
'use strict';
export const SAVE: string = 'storage__save';
export const READY: string = 'storage__ready';
export const ERROR: string = 'storage__error';

@ -0,0 +1,13 @@
export const ON_PASSPHRASE_CHANGE: string = 'action__on_passphrase_change';
export const ON_PASSPHRASE_SHOW: string = 'action__on_passphrase_show';
export const ON_PASSPHRASE_HIDE: string = 'action__on_passphrase_hide';
export const ON_PASSPHRASE_SAVE: string = 'action__on_passphrase_save';
export const ON_PASSPHRASE_FORGET: string = 'action__on_passphrase_forget';
export const ON_PASSPHRASE_FOCUS: string = 'action__on_passphrase_focus';
export const ON_PASSPHRASE_BLUR: string = 'action__on_passphrase_blur';
export const ON_PASSPHRASE_SUBMIT: string = 'action__on_passphrase_submit';
export const FORGET: string = 'modal__forget';
export const REMEMBER: string = 'modal__remember';
export const ON_FORGET: string = 'modal__on_forget';
export const ON_REMEMBER: string = 'modal__on_remember';

@ -0,0 +1,19 @@
/* @flow */
'use strict';
export const INIT: string = 'send__init';
export const DISPOSE: string = 'send__dispose';
export const VALIDATION: string = 'send__validation';
export const ADDRESS_CHANGE: string = 'send__address_change';
export const AMOUNT_CHANGE: string = 'send__amount_change';
export const SET_MAX: string = 'send__set_max';
export const CURRENCY_CHANGE: string = 'send__currency_change';
export const FEE_LEVEL_CHANGE: string = 'send__fee_level_change';
export const GAS_PRICE_CHANGE: string = 'send__gas_price_change';
export const GAS_LIMIT_CHANGE: string = 'send__gas_limit_change';
export const UPDATE_FEE_LEVELS: string = 'send__update_fee_levels';
export const DATA_CHANGE: string = 'send__data_change';
export const SEND: string = 'send__submit';
export const TX_COMPLETE: string = 'send__tx_complete';
export const TX_ERROR: string = 'send__tx_error';
export const TOGGLE_ADVANCED: string = 'send__toggle_advanced';

@ -0,0 +1,7 @@
/* @flow */
'use strict';
export const ADD: string = 'token__add';
export const REMOVE: string = 'token__remove';
export const SET_BALANCE: string = 'token__set_balance';
export const FROM_STORAGE: string = 'token__from_storage';

@ -0,0 +1,26 @@
/* @flow */
'use strict';
export const READY: string = 'trezorconnect__ready';
export const INITIALIZATION_ERROR: string = 'trezorconnect__init_error';
export const SELECT_DEVICE: string = 'trezorconnect__select_device';
export const DEVICE_FROM_STORAGE: string = 'trezorconnect__device_from_storage';
export const AUTH_DEVICE: string = 'trezorconnect__auth_device';
export const COIN_CHANGED: string = 'trezorconnect__coin_changed';
export const REMEMBER_REQUEST: string = 'trezorconnect__remember_request';
export const FORGET_REQUEST: string = 'trezorconnect__forget_request';
export const FORGET: string = 'trezorconnect__forget';
export const FORGET_SINGLE: string = 'trezorconnect__forget_single';
export const DISCONNECT_REQUEST: string = 'trezorconnect__disconnect_request';
export const REMEMBER: string = 'trezorconnect__remember';
export const START_ACQUIRING: string = 'trezorconnect__start_acquiring';
export const STOP_ACQUIRING: string = 'trezorconnect__stop_acquiring';
export const TRY_TO_DUPLICATE: string = 'trezorconnect__try_to_duplicate';
export const DUPLICATE: string = 'trezorconnect__duplicate';
export const DEVICE_STATE_EXCEPTION: string = 'trezorconnect__device_state_exception';

@ -0,0 +1,9 @@
/* @flow */
'use strict';
export const START: string = 'web3__start';
export const STOP: string = 'web3__stop';
export const CREATE: string = 'web3__create';
export const READY: string = 'web3__ready';
export const BLOCK_UPDATED: string = 'web3__block_updated';
export const GAS_PRICE_UPDATED: string = 'web3__gas_price_updated';

@ -0,0 +1,11 @@
/* @flow */
'use strict';
export const INIT: string = 'account__init';
export const DISPOSE: string = 'account__dispose';
export const CREATE: string = 'address__create';
export const REMOVE: string = 'address__remove';
export const SET_BALANCE: string = 'address__set_balance';
export const SET_NONCE: string = 'address__set_nonce';
export const FROM_STORAGE: string = 'address__from_storage';

@ -0,0 +1,9 @@
/* @flow */
'use strict';
export const CREATE: string = 'address__create';
export const REMOVE: string = 'address__remove';
export const SET_BALANCE: string = 'address__set_balance';
export const SET_NONCE: string = 'address__set_nonce';
export const FROM_STORAGE: string = 'address__from_storage';

@ -0,0 +1,6 @@
/* @flow */
'use strict';
export const ADD: string = 'notification__add';
export const CLOSE: string = 'notification__close';
export const REMOVE: string = 'account__remove';

@ -0,0 +1,8 @@
/* @flow */
'use strict';
export const INIT: string = 'receive__init';
export const DISPOSE: string = 'receive__dispose';
export const REQUEST_UNVERIFIED: string = 'receive__request_unverified';
export const SHOW_ADDRESS: string = 'receive__show_address';
export const SHOW_UNVERIFIED_ADDRESS: string = 'receive__show_unverified';

@ -0,0 +1,7 @@
/* @flow */
'use strict';
export const INIT: string = 'summary__init';
export const DISPOSE: string = 'summary__dispose';
export const ADD_TOKEN: string = 'summary__add_token';
export const DETAILS_TOGGLE: string = 'summary__details_toggle';

@ -3,41 +3,44 @@
export const CLOSE_MODAL: string = 'action__close_modal';
export const ON_PIN_ADD: string = 'action__on_pin_click';
export const ON_PIN_BACKSPACE: string = 'action__on_pin_backspace';
export const ON_PIN_SUBMIT: string = 'action__on_pin_submit';
export const ON_PASSPHRASE_CHANGE: string = 'action__on_passphrase_change';
export const ON_PASSPHRASE_SHOW: string = 'action__on_passphrase_show';
export const ON_PASSPHRASE_HIDE: string = 'action__on_passphrase_hide';
export const ON_PASSPHRASE_SAVE: string = 'action__on_passphrase_save';
export const ON_PASSPHRASE_FORGET: string = 'action__on_passphrase_forget';
export const ON_PASSPHRASE_FOCUS: string = 'action__on_passphrase_focus';
export const ON_PASSPHRASE_BLUR: string = 'action__on_passphrase_blur';
export const ON_PASSPHRASE_SUBMIT: string = 'action__on_passphrase_submit';
export const ON_CHANGE_ACCOUNT: string = 'action__on_change_account';
export const ON_CUSTOM_FEE_OPEN: string = 'action__on_custom_fee_open';
export const ON_CUSTOM_FEE_CHANGE: string = 'action__on_custom_fee_change';
export const ON_SELECT_DEVICE: string = 'action__on_select_device';
export const ON_ADDRESS_CHANGE: string = 'send__on_address_change';
export const ON_AMOUNT_CHANGE: string = 'send__on_amount_change';
export const ON_FEE_LEVEL_CHANGE: string = 'send__on_fee_level_change';
export const ON_GAS_PRICE_CHANGE: string = 'send__on_gas_price_change';
export const ON_GAS_LIMIT_CHANGE: string = 'send__on_gas_limit_change';
export const ON_TX_DATA_CHANGE: string = 'send__on_data_change';
export const ON_TX_SEND: string = 'send__on_send';
export const ON_TX_COMPLETE: string = 'send__on_tx_complete';
export const ON_GAS_PRICE_UPDATE: string = 'send__on_gas_price_update';
export const ADDRESS_CREATE: string = 'address__create';
export const ADDRESS_DELETE: string = 'address__delete';
export const ADDRESS_SET_BALANCE: string = 'address__set_balance';
export const ADDRESS_SET_BALANCE: string = 'address2__set_balance';
export const ADDRESS_SET_HISTORY: string = 'address__set_history';
export const ADDRESS_UPDATE_BALANCE: string = 'address__update_balance';
export const ADDRESS_ADD_TO_HISTORY: string = 'address__add_to_history';
export const TX_STATUS_OK: string = 'tx__status_ok';
export const TX_STATUS_ERROR: string = 'tx__status_error';
export const TX_STATUS_UNKNOWN: string = 'tx__status_unknown';
export const TX_STATUS_UNKNOWN: string = 'tx__status_unknown';
export const TX_CONFIRMED: string = 'tx__confirmed';
export const TOKENS_TOGGLE_SUMMARY: string = 'tokens_toggle_summary';
export const TOKENS_SEARCH: string = 'tokens_search';
export const TOKENS_CUSTOM_TOGGLE: string = 'tokens_custom_toggle';
export const TOKENS_CUSTOM_ADDRESS_CHANGE: string = 'tokens_custom_address_change';
export const TOKENS_CUSTOM_NAME_CHANGE: string = 'tokens_custom_name_change';
export const TOKENS_CUSTOM_SHORTCUT_CHANGE: string = 'tokens_custom_shortcut_change';
export const TOKENS_CUSTOM_DECIMAL_CHANGE: string = 'tokens_custom_decimal_change';
export const TOKENS_CUSTOM_ADD: string = 'tokens_custom_add';

@ -1,27 +0,0 @@
/* @flow */
'use strict';
import React from 'react';
import { NavLink } from 'react-router-dom';
const AddressMenu = (props): any => {
const { addresses } = props.addresses;
let accounts = addresses.map((address, i) => {
return (
<NavLink key={i} activeClassName="selected" to={ `/address/${i}` }>
{ `Address #${(address.index + 1 )}` }
<span>{ address.balance } ETH</span>
</NavLink>
)
})
return (
<section className="accounts">
{ accounts }
</section>
);
}
export default AddressMenu;

@ -1,27 +0,0 @@
/* @flow */
'use strict';
import React from 'react';
import { Link } from 'react-router-dom';
const AddressTab = (props): any => {
const urlParams = props.match.params;
const basePath = `/address/${urlParams.address}`;
return (
<div className="address-menu">
<Link to={ `${basePath}` }>
History
</Link>
<Link to={ `${basePath}/send` }>
Send
</Link>
<Link to={ `${basePath}/receive` }>
Receive
</Link>
</div>
);
}
export default AddressTab;

@ -1,42 +0,0 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
export default class Devices extends Component {
render() {
const { devices, selectedDevice } = this.props.connect;
const deviceList: Array<any> = devices.map((dev, index) => {
let css: string = "";
if (dev.unacquired) {
css += "unacquired";
}
if (dev.isUsedElsewhere) {
css += " used-elsewhere";
}
if (dev.featuresNeedsReload) {
css += " reload-features";
}
if (dev.path === selectedDevice) {
css += " active";
}
return (<li key={index} className={css} onClick={ event => this.props.onSelectDevice(dev.path) } >{ dev.label }</li>);
});
if (deviceList.length === 0) {
deviceList.push(
(<li key={0}>No connected devices</li>)
);
}
return (
<nav>
<div className="layout-wrapper">
<ul>
{ deviceList }
</ul>
</div>
</nav>
);
}
}

@ -1,17 +0,0 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
export default class Footer extends Component {
render() {
return (
<footer>
<div className="layout-wrapper">
<span>© 2017</span>
<a href="http://satoshilabs.com">SatoshiLabs</a>
</div>
</footer>
);
}
}

@ -1,20 +0,0 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
export default class Main extends Component {
render() {
return (
<main>
<section className="methods">
<ul className="accounts">
</ul>
</section>
<section className="method-content">
{ this.props.children }
</section>
</main>
);
}
}

@ -1,30 +0,0 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import AddressTab from './AddressTab';
import { QRCode } from 'react-qr-svg';
const History = (props): any => {
const { addresses } = props.addresses;
const currentAddress = addresses[ parseInt(props.match.params.address) ];
if (!currentAddress) return null;
return (
<section className="receive">
<AddressTab match={ props.match } />
<h3>{ currentAddress.address }</h3>
<QRCode
bgColor="#FFFFFF"
fgColor="#000000"
level="Q"
style={{ width: 256 }}
value={ currentAddress.address }
/>
</section>
);
}
export default History;

@ -1,72 +0,0 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import AddressTab from './AddressTab';
const SendForm = (props): any => {
console.log("ENDFORM", props)
const addressId = parseInt( props.match.params.address );
const {
address,
amount,
gasPrice,
gasLimit,
data
} = props.sendForm;
const {
onAddressChange,
onAmountChange,
onGasPriceChange,
onGasLimitChange,
onDataChange,
onSend
} = props.sendFormActions;
const disabled = false;
return (
<section className="send-form">
<AddressTab match={ props.match } />
<div className="row">
<label>Amount</label>
<input type="text" value={ address } onChange={ event => onAddressChange(event.target.value) } />
</div>
<div className="row">
<label>Address</label>
<input type="text" value={ amount } onChange={ event => onAmountChange(event.target.value) } />
</div>
<div className="row">
<label>Gas limit</label>
<input type="text" value={ gasLimit } onChange={ event => onGasLimitChange(event.target.value) } />
</div>
<div className="row">
<label>Gas price</label>
<input type="text" value={ gasPrice } onChange={ event => onGasPriceChange(event.target.value) } />
GWEI
</div>
<div className="row">
<label>Data</label>
<input type="text" value={ data } onChange={ event => onDataChange(event.target.value) } />
</div>
<div className="row">
<label></label>
<button disabled={ disabled } onClick={ event => onSend(addressId) }>SEND</button>
</div>
</section>
);
}
export default SendForm;

@ -0,0 +1,17 @@
/* @flow */
'use strict';
import React from 'react';
const Footer = (props: any): any => {
return (
<footer>
<span>© 2018</span>
<a href="http://satoshilabs.com" target="_blank" className="satoshi green">SatoshiLabs</a>
<a href="tos.pdf" target="_blank" className="green">Terms</a>
<a onClick={ props.showLog } className="green">Show Log</a>
</footer>
);
}
export default Footer;

@ -9,7 +9,6 @@ export default class Header extends Component {
<header>
<div className="layout-wrapper">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2567.5 722.3" width="100%" height="100%" preserveAspectRatio="xMinYMin meet"><path d="M1186 2932.6h46.2v147H1186v-147z"></path><path d="M249 0C149.9 0 69.7 80.2 69.7 179.3v67.2C34.9 252.8 0 261.2 0 272.1v350.7s0 9.7 10.9 14.3c39.5 16 194.9 71 230.6 83.6 4.6 1.7 5.9 1.7 7.1 1.7 1.7 0 2.5 0 7.1-1.7 35.7-12.6 191.5-67.6 231-83.6 10.1-4.2 10.5-13.9 10.5-13.9V272.1c0-10.9-34.4-19.7-69.3-25.6v-67.2C428.4 80.2 347.7 0 249 0zm0 85.7c58.4 0 93.7 35.3 93.7 93.7v58.4c-65.5-4.6-121.4-4.6-187.3 0v-58.4c0-58.5 35.3-93.7 93.6-93.7zm-.4 238.1c81.5 0 149.9 6.3 149.9 17.6v218.8c0 3.4-.4 3.8-3.4 5-2.9 1.3-139 50.4-139 50.4s-5.5 1.7-7.1 1.7c-1.7 0-7.1-2.1-7.1-2.1s-136.1-49.1-139-50.4-3.4-1.7-3.4-5V341c-.8-11.3 67.6-17.2 149.1-17.2z"></path><g transform="translate(91.363 -287.434) scale(.95575)"><path d="M666.6 890V639.3H575v-89.9h285.6v89.9h-90.7V890H666.6z"></path><path d="M1092 890l-47-107.1h-37.4V890H904.3V549.4h181.8c79.8 0 122.6 52.9 122.6 116.7 0 58.8-34 89.9-61.3 103.3l61.7 120.5H1092zm12.2-223.9c0-18.5-16.4-26.5-33.6-26.5h-63v53.8h63c17.2-.4 33.6-8.4 33.6-27.3z"></path><path d="M1262.9 890V549.4h258.3v89.9h-155.4v33.6h151.6v89.9h-151.6v37.4h155.4V890h-258.3z"></path><path d="M1574.9 890.4v-81.9l129.8-168.8h-129.8v-89.9h265.8v81.1l-130.2 169.7h134v89.9l-269.6-.1z"></path><path d="M1869.7 720.3c0-104.6 81.1-176.4 186.5-176.4 105 0 186.5 71.4 186.5 176.4 0 104.6-81.1 176-186.5 176s-186.5-71.4-186.5-176zm268 0c0-47.5-32.3-85.3-81.9-85.3-49.6 0-81.9 37.8-81.9 85.3s32.3 85.3 81.9 85.3c50 0 81.9-37.8 81.9-85.3z"></path><path d="M2473.6 890.4l-47-107.1h-37.4v107.1h-103.3V549.8h181.8c79.8 0 122.6 52.9 122.6 116.7 0 58.8-34 89.9-61.3 103.3l61.7 120.5h-117.1zm12.6-224.3c0-18.5-16.4-26.5-33.6-26.5h-63v53.8h63c17.3-.4 33.6-8.4 33.6-27.3z"></path></g></svg>
<span>TrezorConnect</span>
</div>
</header>
);

@ -0,0 +1,22 @@
/* @flow */
'use strict';
import React from 'react';
export default (props: any): any => {
const style = {
width: `${props.size}px`,
height: `${props.size}px`,
}
return (
<div className="loader-circle" style={ style }>
<p>{ props.label }</p>
<svg className="circular" viewBox="25 25 50 50">
<circle className="route" cx="50" cy="50" r="20" fill="none" stroke="" strokeWidth="1" strokeMiterlimit="10" />
<circle className="path" cx="50" cy="50" r="20" fill="none" strokeWidth="1" strokeMiterlimit="10" />
</svg>
</div>
);
}

@ -0,0 +1,40 @@
/* @flow */
'use strict';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as SendFormActions from '../../actions/SendFormActions';
import { getAddress } from '../../actions/TrezorConnectActions';
const Log = (props: any) => {
return (
<details className="log">
Log
</details>
)
}
function mapStateToProps(state, own) {
}
function mapDispatchToProps(dispatch) {
}
export default connect(
(state) => {
return {
accounts: state.accounts,
receive: state.receive
};
},
(dispatch) => {
return {
getAddress: bindActionCreators(getAddress, dispatch),
};
}
)(Log);

@ -0,0 +1,74 @@
/* @flow */
'use strict';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as NOTIFICATION from '../../actions/constants/notification';
export const Notification = (props: any) => {
const className = `notification ${ props.className }`;
const actionButtons = !props.actions ? null : props.actions.map((a, i) => {
return (
<button key={ i } onClick={ event => { props.close(); a.callback(); } } className="transparent">{ a.label }</button>
)
});
return (
<div className={ className }>
{ props.cancelable ? (
<button className="notification-close transparent"
onClick={ event => props.close() }></button>
) : null }
<div className="notification-body">
<h2>{ props.title }</h2>
<p dangerouslySetInnerHTML={{__html: props.message }}></p>
</div>
{ props.actions && props.actions.length > 0 ? (
<div className="notification-action">
{ actionButtons }
</div>
) : null }
</div>
)
}
export const NotificationGroup = (props: any) => {
const { notifications, close } = props;
return notifications.map((n, i) => {
return (
<Notification
key={i}
className={ n.type }
title={ n.title }
message={ n.message }
cancelable={ n.cancelable }
actions={ n.actions }
close={ close }
/>
)
});
}
export default connect(
(state) => {
return {
notifications: state.notifications
};
},
(dispatch) => {
return {
close: bindActionCreators((notif) => {
return {
type: NOTIFICATION.CLOSE,
payload: notif
}
}, dispatch),
};
}
)(NotificationGroup);

@ -0,0 +1,36 @@
/* @flow */
'use strict';
import React from 'react';
import Header from '../common/Header';
import Footer from '../common/Footer';
export default (props: any): any => {
return (
<div className="app connect-device">
<Header />
<main>
<h2>The private bank in your hands.</h2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row">
<p className="connect">
<svg width="20px" height="57px" viewBox="0 0 20 57">
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5"></rect>
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11"></rect>
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z"></path>
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625"></rect>
</g>
</svg>
<span>Connect TREZOR to continue</span>
</p>
{/* <p>Don't have TREZOR? <a href="https://trezor.io/" target="_blank">Get one</a></p> */}
</div>
<div className="image"></div>
<p>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank">Get one</a></p>
</main>
<Footer />
</div>
);
}

@ -0,0 +1,73 @@
/* @flow */
'use strict';
import React from 'react';
import Preloader from './Preloader';
import ConnectDevice from './ConnectDevice';
import LocalStorageError from './LocalStorageError';
import TrezorConnectError from './TrezorConnectError';
import Header from '../common/Header';
import Footer from '../common/Footer';
import { Notification } from '../common/Notification';
export default (props: any): any => {
const web3 = props.web3;
const { devices } = props.connect;
const localStorageError = props.localStorage.error;
const connectError = props.connect.error;
let notification = null;
if (localStorageError) {
notification = (<Notification
title="Error"
message="Some files are missing"
className="error"
/>);
}
if (connectError) {
notification = (<Notification
title="Error"
message={ connectError }
className="error"
/>);
}
if (notification || (web3.length > 0 && devices.length < 1)) {
return (
<div className="app connect-device">
<Header />
{ notification }
<main>
<h2 className="claim">The private bank in your hands.</h2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row">
<p className="connect">
<span>
<svg width="12px" height="35px" viewBox="0 0 20 57">
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5"></rect>
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11"></rect>
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z"></path>
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625"></rect>
</g>
</svg>
Connect TREZOR to continue
</span>
</p>
{/* <button>Add new device</button> */}
{/* <p>Don't have TREZOR? <a href="https://trezor.io/" target="_blank">Get one</a></p> */}
</div>
<div className="image"></div>
<p>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank">Get one</a></p>
</main>
<Footer />
</div>
);
} else {
return (<Preloader />);
}
}

@ -0,0 +1,12 @@
/* @flow */
'use strict';
import React from 'react';
export default (props: any): any => {
return (
<section className="landing">
localstorage ERROR
</section>
);
}

@ -0,0 +1,13 @@
/* @flow */
'use strict';
import React from 'react';
import Loader from '../common/LoaderCircle';
export default (props: any): any => {
return (
<section className="landing">
<Loader label="Loading" size="100" />
</section>
);
}

@ -0,0 +1,12 @@
/* @flow */
'use strict';
import React from 'react';
export default (props: any): any => {
return (
<section className="landing">
connect ERROR
</section>
);
}

@ -1,41 +0,0 @@
/* @flow */
'use strict';
import React from 'react';
import { formatAmount } from '../../utils/formatUtils';
const AccountSelection = (props): any => {
const { accounts, coinInfo, complete } = props.modal;
const accountsCollection = accounts.map((a, index) => {
let accountStatus: string = a.fresh ? 'Fresh account' : formatAmount(a.balance, coinInfo);
// Loading...
return (
<div key={ index } className="account account_default">
<button onClick={ event => props.modalActions.onAccountSelect(index) }>
<span className="account_title">{ a.label }</span>
<span className="account_status">{ accountStatus }</span>
</button>
</div>
)
});
const header: string = complete ? `Select ${ coinInfo.label } account` : `Loading ${ coinInfo.label } accounts...`;
return (
<div>
<h3>{ header }</h3>
<div className="account_type_tabs">
<div data-tab="normal" className="account_type_tab account_type_normal active">Accounts</div>
<div data-tab="legacy" className="account_type_tab account_type_legacy">Legacy Accounts</div>
</div>
<div className="accounts_list">
{ accountsCollection }
</div>
</div>
);
}
export default AccountSelection;

@ -0,0 +1,55 @@
/* @flow */
'use strict';
import React from 'react';
import { findSelectedDevice } from '../../reducers/TrezorConnectReducer';
const ConfirmAddress = (props: any): any => {
const account = props.accounts.find(a => a.checksum === props.receive.checksum && a.index === props.receive.accountIndex && a.coin === props.receive.coin);
return (
<div className="confirm-address">
<div className="header">
<h3>Confirm address on TREZOR</h3>
<p>Please compare your address on device with address shown bellow.</p>
</div>
<div className="content">
<p>{ account.address }</p>
<label>{ account.coin.toUpperCase() } account #{ (account.index + 1) }</label>
</div>
</div>
);
}
export default ConfirmAddress;
export const ConfirmUnverifiedAddress = (props: any): any => {
const account = props.accounts.find(a => a.checksum === props.receive.checksum && a.index === props.receive.accountIndex && a.coin === props.receive.coin);
const {
onCancel
} = props.modalActions;
const {
showUnverifiedAddress,
showAddress
} = props.receiveActions;
return (
<div className="confirm-address-unverified">
<button className="close-modal transparent" onClick={ onCancel }></button>
<h3>Your TREZOR is not connected</h3>
<p>To prevent phishing attacks, you should verify the address on your TREZOR first. Please reconnect your device to continue with the verification process.</p>
<button onClick={ event => {
onCancel();
showAddress(account.addressPath);
} }>Try again</button>
<button className="white" onClick={ event => {
onCancel();
showUnverifiedAddress();
} }>Show unverified address</button>
</div>
);
}

@ -0,0 +1,34 @@
/* @flow */
'use strict';
import React from 'react';
const Confirmation = (props): any => {
const {
amount,
address,
coin,
token,
total,
selectedFeeLevel
} = props.sendForm;
return (
<div className="confirm-tx">
<div className="header">
<h3>Confirm transaction on your TREZOR</h3>
<p>Details are shown on device</p>
</div>
<div className="content">
<label>Send </label>
<p>{ `${amount} ${token.toUpperCase() }` }</p>
<label>To</label>
<p>{ address }</p>
<label>Fee</label>
<p>{ selectedFeeLevel.label }</p>
</div>
</div>
);
}
export default Confirmation;

@ -1,17 +0,0 @@
/* @flow */
'use strict';
import React from 'react';
const Confirmation = (props): any => {
const { onConfirmation, onConfirmationCancel } = props.modalActions;
return (
<div className="confirmation">
<h3>Confirm</h3>
<button onClick={ onConfirmation }>Export</button>
<button onClick={ onConfirmationCancel }>Cancel</button>
</div>
);
}
export default Confirmation;

@ -0,0 +1,21 @@
/* @flow */
'use strict';
import React from 'react';
const RememberDevice = (props: any): any => {
const { device } = props.modal;
const { onCancel, onDuplicateDevice } = props.modalActions;
return (
<div className="pin">
<h3>Duplicate { device.label } ?</h3>
<label>Device label</label>
<input type="text" />
<button onClick={ onCancel }>Cancel</button>
<button onClick={ event => onDuplicateDevice( { ...device, instanceLabel: "kokot" } ) }>Duplicate</button>
</div>
);
}
export default RememberDevice;

@ -1,95 +0,0 @@
/* @flow */
'use strict';
import React from 'react';
import { formatAmount, formatTime } from '../../utils/formatUtils';
const FeeSelection = (props): any => {
const {
onChangeAccount,
onCustomFeeOpen,
onCustomFeeChange,
onFeeSelect
} = props.modalActions;
const {
feeList,
coinInfo,
customFeeOpened,
customFee
} = props.modal;
const feesCollection = feeList.map((feeItem, index) => {
// skip custom
if (feeItem.name === 'custom') return null;
let feeName;
if (feeItem.name === 'normal' && feeItem.bytes > 0) {
feeName = (
<div>
<span className="fee-name-normal">{ feeItem.name }</span>
<span className="fee-name-subtitle">recommended</span>
</div>
);
} else {
feeName = (<span className="fee-name">{ feeItem.name }</span>);
}
let feeButton: string;
if (feeItem.fee > 0) {
return (
<div className="fee" key={ index }>
<button onClick={ event => onFeeSelect(index) }>
{ feeName }
<span className="fee-size">{ formatAmount(feeItem.fee, coinInfo) }</span>
<span className="fee-minutes">{ formatTime(feeItem.minutes) }</span>
</button>
</div>
);
} else {
return (
<div className="fee insufficient-funds" key={ index }>
<button disabled>
{ feeName }
<span className="fee-insufficient-funds">Insufficient funds</span>
</button>
</div>
);
}
});
return (
<div className="select-fee">
<h3>Select fee:</h3>
<div className="change_account" onClick={ onChangeAccount }>
<span>Change account</span>
</div>
<div className="select_fee_list">
{ feesCollection }
<div className="fee">
<button className={ `fee-custom-opener ${ customFeeOpened ? 'opened' : 'untouched' }` } onClick={ onCustomFeeOpen }>
<span className="fee-name">custom</span>
<span className="fee-insufficient-funds"></span>
<span className="fee-size"></span>
<span className="fee-minutes"></span>
</button>
<div className={ `fee-custom ${ customFeeOpened ? '' : 'hidden' }` }>
<div className="fee-custom-wrapper">
<input className="text" value={ customFee } data-lpignore="true" onChange={ event => onCustomFeeChange(event.target.value) } />
<div className="fee-custom-label">sat/B</div>
<button className="fee-custom-button" disabled="disabled">SEND</button>
</div>
<div className="fee-custom-warning">
<strong>Setting custom fee is not recommended.</strong>
If you set too low fee, it might get stuck forever.
</div>
</div>
</div>
</div>
</div>
);
}
export default FeeSelection;

@ -4,9 +4,11 @@
import React from 'react';
const InvalidPin = (props): any => {
const { device } = props.modal;
return (
<div className="invalid_pin">
<h3>Entered PIN is not correct. Retrying...</h3>
<div className="pin">
<h3>Entered PIN for { device.label } is not correct.</h3>
<p>Retrying...</p>
</div>
);
}

@ -1,100 +0,0 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import { CSSTransition, Transition } from 'react-transition-group';
import { UI } from 'trezor-connect';
import Pin from './Pin';
import InvalidPin from './InvalidPin';
import Passphrase from './Passphrase';
import Permission from './Permission';
import Confirmation from './Confirmation';
import AccountSelection from './AccountSelection';
import FeeSelection from './FeeSelection';
const duration = 300;
const defaultStyle = {
transition: `opacity ${duration}ms ease-in-out`,
opacity: 0,
padding: 20,
display: 'inline-block',
backgroundColor: '#8787d8'
}
const transitionStyles = {
entering: { opacity: 0 },
entered: { opacity: 1 },
};
const Fade2 = ({ in: inProp }) => (
<Transition in={inProp} timeout={duration}>
{(state) => (
<div style={{
...defaultStyle,
...transitionStyles[state]
}}>
I'm A fade Transition2
</div>
)}
</Transition>
);
const Fade = ({ children, ...props }) => (
<CSSTransition
{ ...props }
timeout={ 1000 }
classNames="fade">
{ children }
</CSSTransition>
);
export default class Modal extends Component {
render() {
const { opened, windowType } = this.props.modal;
let component = null;
switch(windowType) {
case UI.REQUEST_PIN :
component = (<Pin { ...this.props } />);
break;
case UI.INVALID_PIN :
component = (<InvalidPin />);
break;
case UI.REQUEST_PASSPHRASE :
component = (<Passphrase { ...this.props } />);
break;
case UI.REQUEST_PERMISSION :
component = (<Permission { ...this.props } />);
break;
case UI.REQUEST_CONFIRMATION :
component = (<Confirmation { ...this.props } />);
break;
case UI.SELECT_ACCOUNT :
component = (<AccountSelection { ...this.props } />);
break;
case UI.SELECT_FEE :
component = (<FeeSelection { ...this.props } />);
break;
}
let ch = null;
if (opened) {
ch = (
<Fade key="1">
<div className="modal-container">
<div className="modal-window">
{ component }
</div>
</div>
</Fade>
);
}
return ch;
}
}

@ -0,0 +1,119 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { CSSTransition, Transition } from 'react-transition-group';
import { UI } from 'trezor-connect';
import * as ModalActions from '../../actions/ModalActions';
import * as ReceiveActions from '../../actions/ReceiveActions';
import Pin from './Pin';
import InvalidPin from './InvalidPin';
import Passphrase from './Passphrase';
import ConfirmSignTx from './ConfirmSignTx';
import ConfirmAddress, { ConfirmUnverifiedAddress } from './ConfirmAddress';
import RememberDevice, { ForgetDevice, DisconnectDevice } from './RememberDevice';
import DuplicateDevice from './DuplicateDevice';
import * as RECEIVE from '../../actions/constants/receive';
import * as MODAL from '../../actions/constants/Modal';
import * as CONNECT from '../../actions/constants/TrezorConnect';
const duration = 300;
const Fade = ({ children, ...props }) => (
<CSSTransition
{ ...props }
timeout={ 1000 }
classNames="fade">
{ children }
</CSSTransition>
);
class Modal extends Component {
render() {
const { opened, windowType } = this.props.modal;
let component = null;
switch (windowType) {
case UI.REQUEST_PIN :
component = (<Pin { ...this.props } />);
break;
case UI.INVALID_PIN :
component = (<InvalidPin { ...this.props } />);
break;
case UI.REQUEST_PASSPHRASE :
component = (<Passphrase { ...this.props } />);
break;
case "ButtonRequest_SignTx" :
component = (<ConfirmSignTx { ...this.props } />)
break;
case "ButtonRequest_Address" :
component = (<ConfirmAddress { ...this.props } />)
break;
case RECEIVE.REQUEST_UNVERIFIED :
component = (<ConfirmUnverifiedAddress { ...this.props } />)
break;
case CONNECT.REMEMBER_REQUEST :
component = (<RememberDevice { ...this.props } />)
break;
case CONNECT.FORGET_REQUEST :
component = (<ForgetDevice { ...this.props } />)
break;
case CONNECT.DISCONNECT_REQUEST :
component = (<DisconnectDevice { ...this.props } />)
break;
case CONNECT.TRY_TO_DUPLICATE :
component = (<DuplicateDevice { ...this.props } />)
break;
}
let ch = null;
if (opened) {
ch = (
<Fade key="1">
<div className="modal-container">
<div className="modal-window">
{ component }
</div>
</div>
</Fade>
);
}
return ch;
}
}
const mapStateToProps = (state: any, own: any): any => {
return {
modal: state.modal,
accounts: state.accounts,
devices: state.connect.devices,
sendForm: state.sendForm,
receive: state.receive,
};
}
const mapDispatchToProps = (dispatch: any): any => {
return {
modalActions: bindActionCreators(ModalActions, dispatch),
receiveActions: bindActionCreators(ReceiveActions, dispatch),
};
}
// export default connect(mapStateToProps, mapDispatchToProps)(Modal);
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Modal)
);

@ -1,94 +1,308 @@
/* @flow */
'use strict';
import React, { Component, KeyboardEvent, FocusEvent } from 'react';
import React, { Component } from 'react';
import raf from 'raf';
type State = {
singleInput: boolean;
passphrase: string;
passphraseRevision: string;
passphraseFocused: boolean;
passphraseRevisionFocused: boolean;
passphraseRevisionTouched: boolean;
match: boolean;
visible: boolean;
}
export default class PinModal extends Component {
input: HTMLInputElement;
state: State;
passphraseInput: HTMLInputElement;
passphraseRevisionInput: HTMLInputElement;
constructor(props: any) {
super(props);
console.warn("PROPZ", props)
const isSavedDevice = props.devices.find(d => d.path === props.modal.device.path && d.remember);
this.state = {
singleInput: isSavedDevice ? true : false,
passphrase: '',
passphraseRevision: '',
passphraseFocused: false,
passphraseRevisionFocused: false,
passphraseRevisionTouched: false,
match: true,
visible: false
}
}
keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) {
event.preventDefault();
//this.passphraseInput.blur();
//this.passphraseRevisionInput.blur();
//this.passphraseInput.type = 'text';
//this.passphraseRevisionInput.type = 'text';
this.submit();
// TODO: set timeout, or wait for blur event
//onPassphraseSubmit(passphrase, passphraseCached);
//raf(() => onPassphraseSubmit(passphrase));
}
}
componentDidMount(): void {
// one time autofocus
this.input.focus();
this.passphraseInput.focus();
this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false);
// document.oncontextmenu = (event) => {
// const el = window.event.srcElement || event.target;
// const type = el.tagName.toLowerCase() || '';
// if (type === 'input') {
// return false;
// }
// };
}
componentWillUnmount(): void {
window.removeEventListener('keydown', this.keyboardHandler, false);
// this.passphraseInput.type = 'text';
// this.passphraseInput.style.display = 'none';
// this.passphraseRevisionInput.type = 'text';
// this.passphraseRevisionInput.style.display = 'none';
}
keyboardHandler(event: KeyboardEvent): void {
const { onPassphraseSubmit } = this.props;
const { passphrase, passphraseCached } = this.props.modal;
if (event.keyCode === 13) {
event.preventDefault();
this.input.blur();
onPassphraseSubmit(passphrase, passphraseCached);
}
}
// we don't want to keep password inside "value" attribute,
// so we need to replace it thru javascript
componentDidUpdate() {
const { passphrase, passphraseFocused, passphraseVisible } = this.props.modal;
let inputValue: string = passphrase;
if (!passphraseVisible && !passphraseFocused) {
inputValue = passphrase.replace(/./g, '•');
const {
passphrase,
passphraseRevision,
passphraseFocused,
passphraseRevisionFocused,
visible
} = this.state;
// } = this.props.modal;
let passphraseInputValue: string = passphrase;
let passphraseRevisionInputValue: string = passphraseRevision;
if (!visible && !passphraseFocused) {
passphraseInputValue = passphrase.replace(/./g, '•');
}
if (!visible && !passphraseRevisionFocused) {
passphraseRevisionInputValue = passphraseRevision.replace(/./g, '•');
}
this.passphraseInput.value = passphraseInputValue;
this.passphraseInput.setAttribute("type", visible ? "text" : "password");
if (this.passphraseRevisionInput) {
this.passphraseRevisionInput.value = passphraseRevisionInputValue;
this.passphraseRevisionInput.setAttribute("type", visible ? "text" : "password");
}
}
onPassphraseChange = (input: string, value: string): void => {
// https://codepen.io/MiDri/pen/PGqvrO
// or
// https://github.com/zakangelle/react-password-mask/blob/master/src/index.js
if (input === 'passphrase') {
this.setState({
match: this.state.singleInput || this.state.passphraseRevision === value,
passphrase: value
});
} else {
this.setState({
match: this.state.passphrase === value,
passphraseRevision: value,
passphraseRevisionTouched: true
});
}
}
onPassphraseFocus = (input: string): void => {
if (input === 'passphrase') {
this.setState({
passphraseFocused: true
});
} else {
this.setState({
passphraseRevisionFocused: true
});
}
}
onPassphraseBlur = (input: string): void => {
if (input === 'passphrase') {
this.setState({
passphraseFocused: false
});
} else {
this.setState({
passphraseRevisionFocused: false
});
}
this.input.value = inputValue;
}
render(): void {
onPassphraseShow = (): void => {
this.setState({
visible: true
});
}
onPassphraseHide = (): void => {
this.setState({
visible: false
});
}
submit = (empty: boolean = false): void => {
const { onPassphraseSubmit } = this.props.modalActions;
const { passphrase } = this.state;
//this.passphraseInput.type = 'text';
// this.passphraseInput.style.display = 'none';
//this.passphraseInput.setAttribute('readonly', 'readonly');
// this.passphraseRevisionInput.type = 'text';
//this.passphraseRevisionInput.style.display = 'none';
//this.passphraseRevisionInput.setAttribute('readonly', 'readonly');
const p = passphrase;
this.setState({
passphrase: '',
passphraseRevision: '',
passphraseFocused: false,
passphraseRevisionFocused: false,
visible: false
})
raf(() => onPassphraseSubmit(empty ? '' : passphrase));
}
render(): any {
const {
onPassphraseChange,
onPassphraseSubmit,
onPassphraseForget,
onPassphraseFocus,
onPassphraseBlur,
onPassphraseSave,
onPassphraseShow,
onPassphraseHide
//onPassphraseChange,
//onPassphraseSubmit,
//onPassphraseSubmitEmpty,
//onPassphraseForget,
//onPassphraseFocus,
//onPassphraseBlur,
//onPassphraseSave,
//onPassphraseShow,
//onPassphraseHide
} = this.props.modalActions;
const { passphrase, passphraseFocused, passphraseVisible, passphraseCached } = this.props.modal;
let inputType: string = passphraseVisible || (!passphraseVisible && !passphraseFocused) ? "text" : "password";
const showPassphraseCheckboxFn: Function = passphraseVisible ? onPassphraseHide : onPassphraseShow;
const savePassphraseCheckboxFn: Function = passphraseCached ? onPassphraseForget : onPassphraseSave;
const {
device,
//passphrase,
//passphraseRevision,
//passphraseFocused,
//passphraseRevisionFocused,
//passphraseVisible,
//passphraseMatch,
//passphraseRevisionTouched,
passphraseCached
} = this.props.modal;
const {
singleInput,
passphrase,
passphraseRevision,
passphraseFocused,
passphraseRevisionFocused,
visible,
match,
passphraseRevisionTouched,
} = this.state;
let passphraseInputType: string = visible || (!visible && !passphraseFocused) ? "text" : "password";
let passphraseRevisionInputType: string = visible || (!visible && !passphraseRevisionFocused) ? "text" : "password";
passphraseInputType = passphraseRevisionInputType = "text";
//let passphraseInputType: string = visible || passphraseFocused ? "text" : "password";
//let passphraseRevisionInputType: string = visible || passphraseRevisionFocused ? "text" : "password";
const showPassphraseCheckboxFn: Function = visible ? this.onPassphraseHide : this.onPassphraseShow;
return (
<div className="passphrase">
<h3>Please enter your passphrase.</h3>
<h4>Note that passphrase is case-sensitive.</h4>
<div>
{/* <button className="close-modal transparent" onClick={ event => this.submit(true) }></button> */}
<h3>Enter { device.label } passphrase</h3>
<p>Note that passphrase is case-sensitive.</p>
<div className="row">
<label>Passphrase</label>
<input
ref={ (element) => { this.input = element; } }
onChange={ event => onPassphraseChange(event.currentTarget.value) }
type={ inputType }
ref={ (element) => { this.passphraseInput = element; } }
onChange={ event => this.onPassphraseChange('passphrase', event.currentTarget.value) }
type={ passphraseInputType }
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
data-lpignore="true"
onFocus={ onPassphraseFocus }
onBlur={ onPassphraseBlur }
onFocus={ event => this.onPassphraseFocus('passphrase') }
onBlur={ event => this.onPassphraseBlur('passphrase') }
tabIndex="1" />
</div>
<div className="passphrase_options">
<label>
<input type="checkbox" className="show_passphrase" tabIndex="2" onChange={ showPassphraseCheckboxFn } checked={ passphraseVisible } />
<span>Show passphrase</span>
</label>
<label>
<input type="checkbox" className="save_passphrase" tabIndex="3" onChange={ savePassphraseCheckboxFn } checked={ passphraseCached } />
<span>Save passphrase for current session *</span>
{ singleInput ? null : (
<div className="row">
<label>Re-enter passphrase</label>
<input
ref={ (element) => { this.passphraseRevisionInput = element; } }
onChange={ event => this.onPassphraseChange('revision', event.currentTarget.value) }
type={ passphraseRevisionInputType }
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
data-lpignore="true"
onFocus={ event => this.onPassphraseFocus('revision') }
onBlur={ event => this.onPassphraseBlur('revision') }
tabIndex="2" />
{ !match && passphraseRevisionTouched ? <span className="error">Passphrases do not match</span> : null }
</div>
) }
<div className="row">
<label className="custom-checkbox">
<input type="checkbox" tabIndex="3" onChange={ showPassphraseCheckboxFn } checked={ visible } />
<span className="indicator"></span>
Show passphrase
</label>
{/* <label className="custom-checkbox">
<input type="checkbox" className="save_passphrase" tabIndex="4" onChange={ savePassphraseCheckboxFn } checked={ passphraseCached } />
<span className="indicator"></span>
<span>Save passphrase for current session (i)</span>
</label> */}
</div>
<div>
<button type="button" className="submit" tabIndex="4" disabled={ !match } onClick={ event => this.submit() }>Enter</button>
</div>
<div>
<button type="button" className="submit" tabIndex="4" onClick={ event => onPassphraseSubmit(passphrase, passphraseCached) }>Enter</button>
<p>If you want to access your default account</p>
<p><a className="green" onClick={ event => this.submit(true) }>Leave passphrase blank</a></p>
</div>
</div>
);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save