mirror of https://github.com/BTCPrivate/copay.git
commit
8b1130d66b
|
@ -37,8 +37,7 @@ module.exports = function(grunt) {
|
||||||
'src/js/routes.js',
|
'src/js/routes.js',
|
||||||
'src/js/services/*.js',
|
'src/js/services/*.js',
|
||||||
'src/js/models/*.js',
|
'src/js/models/*.js',
|
||||||
'src/js/controllers/*.js',
|
'src/js/controllers/*.js'
|
||||||
'src/js/trezor.js'
|
|
||||||
],
|
],
|
||||||
tasks: ['concat:js']
|
tasks: ['concat:js']
|
||||||
}
|
}
|
||||||
|
@ -79,7 +78,8 @@ module.exports = function(grunt) {
|
||||||
'src/js/translations.js',
|
'src/js/translations.js',
|
||||||
'src/js/version.js',
|
'src/js/version.js',
|
||||||
'src/js/init.js',
|
'src/js/init.js',
|
||||||
'src/js/trezor.js'
|
'src/js/trezor-url.js',
|
||||||
|
'bower_components/trezor-connect/login.js'
|
||||||
],
|
],
|
||||||
dest: 'public/js/copay.js'
|
dest: 'public/js/copay.js'
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
Copay accepts three base derivation paths:
|
||||||
|
|
||||||
|
* m/44'
|
||||||
|
* m/48' (only used for MULTISIGNATURE, HARDWARE Wallets)
|
||||||
|
* m/45' (deprecated and it is only supported for old wallets)
|
||||||
|
|
||||||
|
Both m/44 and m/48 follow the BIP44 standard:
|
||||||
|
|
||||||
|
m/XX'/<coin_type>'/<account'>
|
||||||
|
|
||||||
|
Supported cointypes are: 0: Livenet, and 1: Testnet
|
||||||
|
|
||||||
|
If you need to import a wallet from a mnemonic using an account different
|
||||||
|
from the default (0), use, for example:
|
||||||
|
|
||||||
|
m/44'/0'/11'
|
||||||
|
|
||||||
|
to import account 11.
|
||||||
|
|
||||||
|
In case you have a multisignature wallet originally created from a hardware device, and you had loose access to the device, you will need to enter the 24 mnemonic backup (from the device) and a path like:
|
||||||
|
|
||||||
|
|
||||||
|
m/48'/0'/8'
|
||||||
|
|
||||||
|
for a multisignature wallet, account 8.
|
||||||
|
|
||||||
|
Finally, note that TREZOR use 1-based account numbers, so if your are trying for example to recover TREZOR multisig account #8, you should enter `m/48'/0'/7'`.
|
|
@ -8,7 +8,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"angular": "1.4.6",
|
"angular": "1.4.6",
|
||||||
"angular-bitcore-wallet-client": "1.1.2",
|
"angular-bitcore-wallet-client": "1.1.6",
|
||||||
"angular-foundation": "0.7.0",
|
"angular-foundation": "0.7.0",
|
||||||
"angular-gettext": "2.1.0",
|
"angular-gettext": "2.1.0",
|
||||||
"angular-moment": "0.10.1",
|
"angular-moment": "0.10.1",
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
"foundation-icon-fonts": "*",
|
"foundation-icon-fonts": "*",
|
||||||
"moment": "2.10.3",
|
"moment": "2.10.3",
|
||||||
"ng-lodash": "0.2.3",
|
"ng-lodash": "0.2.3",
|
||||||
"qrcode-decoder-js": "*"
|
"qrcode-decoder-js": "*",
|
||||||
|
"trezor-connect": "~1.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,12 @@ echo $CMD
|
||||||
$CMD
|
$CMD
|
||||||
checkOK
|
checkOK
|
||||||
|
|
||||||
|
cd $BUILDDIR/../..
|
||||||
|
CMD="rsync -rLRv ./bower_components/trezor-connect/chrome/* $APPDIR"
|
||||||
|
echo $CMD
|
||||||
|
$CMD
|
||||||
|
checkOK
|
||||||
|
|
||||||
# Zipping chrome-extension
|
# Zipping chrome-extension
|
||||||
echo "${OpenColor}${Green}* Zipping all chrome-extension files...${CloseColor}"
|
echo "${OpenColor}${Green}* Zipping all chrome-extension files...${CloseColor}"
|
||||||
cd $BUILDDIR
|
cd $BUILDDIR
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
"notifications",
|
"notifications",
|
||||||
"videoCapture"
|
"videoCapture",
|
||||||
|
"webview"
|
||||||
],
|
],
|
||||||
"app": {
|
"app": {
|
||||||
"background": {
|
"background": {
|
||||||
|
|
|
@ -56,21 +56,19 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div ng-hide="create.hideWalletName">
|
||||||
<div class="large-12 columns" ng-hide="create.hideWalletName">
|
<label><span translate>Wallet name</span>
|
||||||
<label><span translate>Wallet name</span>
|
<div class="input">
|
||||||
<div class="input">
|
<input type="text" placeholder="{{'Family vacation funds'|translate}}" class="form-control" name="walletName" ng-model="walletName" ng-required="true" ng-focus="create.formFocus('wallet-name')" ng-blur="create.formFocus(false)">
|
||||||
<input type="text" placeholder="{{'Family vacation funds'|translate}}" class="form-control" name="walletName" ng-model="walletName" ng-required="true" ng-focus="create.formFocus('wallet-name')" ng-blur="create.formFocus(false)">
|
</div>
|
||||||
</div>
|
</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
<div ng-show="totalCopayers != 1">
|
||||||
<div class="large-12 columns" ng-show="totalCopayers != 1">
|
<label><span translate>Your nickname</span>
|
||||||
<label><span translate>Your nickname</span>
|
<div class="input">
|
||||||
<div class="input">
|
<input type="text" placeholder="{{'John'|translate}}" class="form-control" name="myName" ng-model="myName" ng-required="totalCopayers != 1" ng-disabled="totalCopayers == 1" ng-focus="create.formFocus('my-name')" ng-blur="create.formFocus(false)">
|
||||||
<input type="text" placeholder="{{'John'|translate}}" class="form-control" name="myName" ng-model="myName" ng-required="totalCopayers != 1" ng-disabled="totalCopayers == 1" ng-focus="create.formFocus('my-name')" ng-blur="create.formFocus(false)">
|
</div>
|
||||||
</div>
|
</label>
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row" ng-show="totalCopayers != 1">
|
<div class="row" ng-show="totalCopayers != 1">
|
||||||
<div class="large-6 medium-6 columns">
|
<div class="large-6 medium-6 columns">
|
||||||
|
@ -98,74 +96,80 @@
|
||||||
<i ng-if="!hideAdv" class="icon-arrow-up4"></i>
|
<i ng-if="!hideAdv" class="icon-arrow-up4"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-hide="hideAdv" class="row">
|
<div ng-hide="hideAdv" class="row">
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
|
<div>
|
||||||
<label ng-show="index.isChromeApp && totalCopayers > 1 " for="hw-ledger" class="oh">
|
<label for="bws" class="oh">
|
||||||
<span translate>Use Ledger hardware wallet</span>
|
<span>Wallet Service URL</span>
|
||||||
<switch id="hw-ledger" name="hwLedger" ng-model="hwLedger" ng-change="isTestnet=false" class="green right m5t m10b"></switch>
|
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
||||||
</label>
|
|
||||||
<!-- -->
|
|
||||||
<label ng-show="!index.isCordova && 0" for="hw-trezor" class="oh">
|
|
||||||
<span translate>Use TREZOR hardware wallet</span>
|
|
||||||
<switch id="hw-trezor" name="hwTrezor" ng-model="hwTrezor" class="green right m5t m10b"></switch>
|
|
||||||
</label>
|
|
||||||
<!-- TODO account
|
|
||||||
<div ng-show="hwLedger">
|
|
||||||
<label class="oh"><span translate>Ledger Slot</span>
|
|
||||||
<select class="m10t" ng-model="externalIndex" ng-options="externalIndex as externalIndex for externalIndex in create.externalIndexValues">
|
|
||||||
</select>
|
|
||||||
</label>
|
</label>
|
||||||
<div class="oh text-gray line-b size-12 p10b m20b"><span translate>Ledger supports up to 20 Copay wallets simultaneously. Select which slot should be used to host this wallet</div>
|
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
<label for="network-name" class="oh" ng-show="!hwLedger && !hwTrezor">
|
|
||||||
<span translate>Testnet</span>
|
|
||||||
<switch id="network-name" name="isTestnet" ng-model="isTestnet" class="green right m5t m10b"></switch>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label ng-show="!hwLedger && !hwTrezor" for="seed" class="oh">
|
<div>
|
||||||
<span translate>Specify your wallet seed</span>
|
<label><span translate>Wallet Seed</span>
|
||||||
<switch id="seed" name="setSeed" ng-model="setSeed" class="green right m5t m10b"></switch>
|
<select class="m10t" ng-model="seedSource"
|
||||||
</label>
|
ng-options="seed as seed.label for seed in create.seedOptions"
|
||||||
|
ng-change="create.setSeedSource()">
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<label for="createPassphrase" class="oh" ng-hide="setSeed || hwLedger || hwTrezor" ><span translate>Add a Seed Passphrase</span> <small translate>Add an optional passphrase to secure the seed</small>
|
<div ng-show="create.seedSourceId == 'trezor' || create.seedSourceId == 'ledger'">
|
||||||
<div class="input">
|
<label class="oh"><span translate>Account Number</span>
|
||||||
<input type="text" class="form-control"
|
<input type="number" id="account" ng-model="account">
|
||||||
name="createPassphrase" ng-model="createPassphrase">
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="ext-master" class="m10t" ng-show="setSeed">
|
<div class="box-notification" ng-show="create.seedSourceId=='new' && createPassphrase">
|
||||||
<span translate>Wallet Seed</span>
|
<span class="text-warning size-14">
|
||||||
<small translate>Enter the seed words (BIP39)</small>
|
<i class="fi-alert"></i>
|
||||||
<input id="ext-master"
|
<span translate>
|
||||||
type="text"
|
WARNING: Passphrase cannot be recovered. <b>Be sure to write it down</b>. The wallet can not be restored without the passphrase.
|
||||||
name="privateKey" ng-model="privateKey">
|
</span>
|
||||||
</label>
|
</span>
|
||||||
<label for="passphrase" class="oh" ng-show="setSeed"><span translate>Seed Passphrase</span> <small translate>The seed could require a passphrase to be imported</small>
|
</div>
|
||||||
<div class="input">
|
|
||||||
<input type="text" class="form-control" name="passphrase" ng-model="passphrase">
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="bws" class="oh">
|
<div ng-show="create.seedSourceId=='new' ">
|
||||||
<span>Wallet Service URL</span>
|
<label for="createPassphrase" ><span translate>Add a Seed Passphrase</span> <small translate>Add an optional passphrase to secure the seed</small>
|
||||||
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
<div class="input">
|
||||||
</label>
|
<input type="text" class="form-control"
|
||||||
</div>
|
name="createPassphrase" ng-model="createPassphrase">
|
||||||
</div>
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box-notification" ng-show="!setSeed && createPassphrase">
|
<div ng-show="create.seedSourceId=='set'">
|
||||||
<span class="text-warning size-14">
|
<label for="ext-master">
|
||||||
<i class="fi-alert"></i>
|
<span translate>Wallet Seed</span>
|
||||||
<span translate>
|
<small translate>Enter the seed words (BIP39)</small>
|
||||||
WARNING: Passphrase cannot be recovered. <b>Be sure to write it down</b>. The wallet can not be restored without the passphrase.
|
<input id="ext-master"
|
||||||
</span>
|
type="text"
|
||||||
</span>
|
name="privateKey" ng-model="privateKey">
|
||||||
</div>
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="create.seedSourceId=='set'">
|
||||||
|
<label for="passphrase"> <span translate>Seed Passphrase</span> <small translate>The seed could require a passphrase to be imported</small>
|
||||||
|
<div class="input">
|
||||||
|
<input type="text" class="form-control" name="passphrase" ng-model="passphrase">
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="create.seedSourceId == 'set'">
|
||||||
|
<label class="oh"><span translate>Derivation Path</span> <small translate>BIP32 path for address derivation</small>
|
||||||
|
<input type="text" class="form-control" name="derivationPath" ng-model="derivationPath">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="oh" ng-show="create.seedSourceId == 'new'">
|
||||||
|
<label for="network-name" >
|
||||||
|
<span translate>Testnet</span>
|
||||||
|
<switch id="network-name" name="isTestnet" ng-model="isTestnet" class="green right m5t m10b"></switch>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div> <!-- columns -->
|
||||||
|
</div> <!-- advanced -->
|
||||||
|
|
||||||
<button type="submit" class="button round black expand m0" ng-show="totalCopayers != 1" ng-disabled="setupForm.$invalid || create.loading || create.hwWallet">
|
<button type="submit" class="button round black expand m0" ng-show="totalCopayers != 1" ng-disabled="setupForm.$invalid || create.loading || create.hwWallet">
|
||||||
<span translate>Create {{requiredCopayers}}-of-{{totalCopayers}} wallet</span>
|
<span translate>Create {{requiredCopayers}}-of-{{totalCopayers}} wallet</span>
|
||||||
|
@ -174,8 +178,9 @@
|
||||||
<button type="submit" class="button round black expand m0" ng-show="totalCopayers == 1" ng-disabled="setupForm.$invalid || create.loading || create.hwWallet">
|
<button type="submit" class="button round black expand m0" ng-show="totalCopayers == 1" ng-disabled="setupForm.$invalid || create.loading || create.hwWallet">
|
||||||
<span translate>Create new wallet</span>
|
<span translate>Create new wallet</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
</div> <!-- large-12 columns -->
|
||||||
|
</div> <!-- row -->
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="extra-margin-bottom"></div>
|
<div class="extra-margin-bottom"></div>
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h4></h4>
|
<h4></h4>
|
||||||
<ul class="no-bullet m0" ng-hide="index.isPrivKeyExternal">
|
<ul class="no-bullet m0" ng-show="index.canSign">
|
||||||
<li>
|
<li>
|
||||||
<switch id="no-sign" name="noSign" ng-model="noSign" class="green right"></switch>
|
<switch id="no-sign" name="noSign" ng-model="noSign" class="green right"></switch>
|
||||||
<span translate>Do not include private key</span>
|
<span translate>Do not include private key</span>
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
<div class="box-notification" ng-show="index.isPrivKeyExternal">
|
<div class="box-notification" ng-show="!index.canSign">
|
||||||
<span class="text-warning size-14">
|
<span class="text-warning size-14">
|
||||||
<i class="fi-alert"></i>
|
<i class="fi-alert"></i>
|
||||||
<span translate>
|
<span translate>
|
||||||
|
|
|
@ -30,8 +30,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="create-tab pr small-only-text-center" ng-hide="create.hideTabs">
|
<div class="create-tab pr small-only-text-center" ng-hide="create.hideTabs">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="tab-container small-4 medium-4 large-4" ng-class="{'selected': type =='12'}">
|
<div class="tab-container small-4 medium-4 large-4" ng-class="{'selected': type =='12'}">
|
||||||
|
@ -46,21 +44,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" ng-show="type == '12' ">
|
<div class="row" ng-show="type == '12' ">
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<form name="importForm12" ng-submit="import.importMnemonic(importForm12)" novalidate>
|
<form name="importForm12" ng-submit="import.importMnemonic(importForm12)" novalidate>
|
||||||
<div class="box-notification" ng-show="import.error">
|
<div class="box-notification" ng-show="import.error">
|
||||||
<span class="text-warning size-14">
|
<span class="text-warning size-14">
|
||||||
{{import.error|translate}}
|
{{import.error|translate}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div >
|
<div >
|
||||||
<label for="words">
|
<label for="words">
|
||||||
<span translate>Type the Seed Word (usually 12 words)</span>:
|
<span translate>Type the Seed Word (usually 12 words)</span>:
|
||||||
</label>
|
</label>
|
||||||
<textarea class="form-control" name="words" ng-model="import.words" rows="2"></textarea>
|
<textarea class="form-control" name="words" ng-model="import.words" rows="2"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m10t oh" ng-init="hideAdv=true">
|
<div class="m10t oh" ng-init="hideAdv=true">
|
||||||
<a class="button outline light-gray expand tiny" ng-click="hideAdv=!hideAdv">
|
<a class="button outline light-gray expand tiny" ng-click="hideAdv=!hideAdv">
|
||||||
|
@ -73,18 +71,78 @@
|
||||||
</div>
|
</div>
|
||||||
<div ng-hide="hideAdv" class="row">
|
<div ng-hide="hideAdv" class="row">
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<label for="network-name" class=" oh">
|
|
||||||
<span translate>Testnet</span>
|
|
||||||
<switch id="network-name" name="isTestnet" ng-model="isTestnet" class="green right m5t m10b"></switch>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="passphrase" class="oh"><span translate>Passphrase</span> <small translate>Wallet Seed could require a passphrase to be imported</small>
|
<label for="passphrase" class="oh"><span translate>Passphrase</span> <small translate>Wallet Seed could require a passphrase to be imported</small>
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<input type="password" class="form-control" placeholder="{{'Seed passphrase'|translate}}"
|
<input type="password" class="form-control" placeholder="{{'Seed passphrase'|translate}}"
|
||||||
name="passphrase" ng-model="import.passphrase">
|
name="passphrase" ng-model="import.passphrase">
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="oh"><span translate>Derivation Path</span> <small translate>BIP32 path for address derivation</small>
|
||||||
|
<input type="text" class="form-control" name="derivationPath" ng-model="derivationPath">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="bws" class="oh">
|
||||||
|
<span>Wallet Service URL</span>
|
||||||
|
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button translate type="submit" class="button round expand black m10t"
|
||||||
|
ng-disabled="importForm12.$invalid || import.loading">
|
||||||
|
Import
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" ng-show="type == 'file' ">
|
||||||
|
<div class="large-12 columns">
|
||||||
|
<form name="importForm" ng-submit="import.importBlob(importForm)" novalidate>
|
||||||
|
<div class="box-notification" ng-show="import.error">
|
||||||
|
<span class="text-warning size-14">
|
||||||
|
{{import.error|translate}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="!index.isSafari && !index.isCordova" class="line-b m10b">
|
||||||
|
<label for="backupFile">
|
||||||
|
<span translate>Choose a backup file from your computer</span> <i class="fi-laptop"></i>
|
||||||
|
</label>
|
||||||
|
<input type="file" class="form-control" placeholder="{{'Select a backup file'|translate}}"
|
||||||
|
name="backupFile" ng-model="import.backupFile" ng-file-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="index.isSafari || index.isCordova">
|
||||||
|
<label for="backupText">
|
||||||
|
<span translate>Paste the backup plain text code</span> <i class="fi-clipboard"></i>
|
||||||
|
</label>
|
||||||
|
<textarea class="form-control" name="backupText" ng-model="import.backupText" rows="5"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="password"><span translate>Password</span>
|
||||||
|
</label>
|
||||||
|
<div class="input">
|
||||||
|
<input type="password" class="form-control" placeholder="{{'Your backup password'|translate}}"
|
||||||
|
name="password" ng-model="import.password">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m10t oh" ng-init="hideAdv=true">
|
||||||
|
<a class="button outline light-gray expand tiny" ng-click="hideAdv=!hideAdv">
|
||||||
|
<i class="fi-widget m3r"></i>
|
||||||
|
<span translate ng-hide="!hideAdv">Show advanced options</span>
|
||||||
|
<span translate ng-hide="hideAdv">Hide advanced options</span>
|
||||||
|
<i ng-if="hideAdv" class="icon-arrow-down4"></i>
|
||||||
|
<i ng-if="!hideAdv" class="icon-arrow-up4"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div ng-hide="hideAdv" class="row">
|
||||||
|
<div class="large-12 columns">
|
||||||
|
|
||||||
<label for="bws" class="oh">
|
<label for="bws" class="oh">
|
||||||
<span>Wallet Service URL</span>
|
<span>Wallet Service URL</span>
|
||||||
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
||||||
|
@ -92,45 +150,57 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button translate type="submit" class="button round expand black m10t"
|
<button translate type="submit" class="button round expand black"
|
||||||
ng-disabled="importForm12.$invalid || import.loading">
|
ng-disabled="importForm.$invalid || !import.password || import.loading">
|
||||||
Import
|
Import backup
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
|
<div class="text-center text-gray p20v" ng-click="$root.go('importLegacy')">
|
||||||
|
<p class="text-gray m5b size-14" translate> Have a Backup from Copay v0.9?</p>
|
||||||
|
<button class=" outline dark-gray tiny round"> <span translate>Import here</span>
|
||||||
|
<i class="icon-arrow-right3 size-14"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" ng-show="type == 'hwWallet'">
|
||||||
|
<div class="large-12 columns">
|
||||||
|
<div class="box-notification" ng-show="import.error">
|
||||||
|
<span class="text-warning size-14">
|
||||||
|
{{import.error|translate}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form name="importForm3" ng-submit="import.importHW(importForm3)" novalidate>
|
||||||
<div class="row" ng-show="type == 'file' ">
|
<div class="large-12 columns">
|
||||||
<div class="large-12 columns">
|
<div ng-show="!import.seedOptions[0]">
|
||||||
<form name="importForm" ng-submit="import.importBlob(importForm)" novalidate>
|
<span translate>No harware wallets supported on this device</span>
|
||||||
<div class="box-notification" ng-show="import.error">
|
</div>
|
||||||
<span class="text-warning size-14">
|
<div ng-show="import.seedOptions[0]">
|
||||||
{{import.error|translate}}
|
<div>
|
||||||
</span>
|
<label><span translate>Wallet Seed</span>
|
||||||
</div>
|
<select class="m10t" ng-model="seedSource"
|
||||||
|
ng-options="seed as seed.label for seed in import.seedOptions"
|
||||||
<div ng-show="!index.isSafari && !index.isCordova" class="line-b m10b">
|
ng-change="import.setSeedSource()">
|
||||||
<label for="backupFile">
|
</select>
|
||||||
<span translate>Choose a backup file from your computer</span> <i class="fi-laptop"></i>
|
|
||||||
</label>
|
</label>
|
||||||
<input type="file" class="form-control" placeholder="{{'Select a backup file'|translate}}"
|
|
||||||
name="backupFile" ng-model="import.backupFile" ng-file-select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-show="index.isSafari || index.isCordova">
|
<div ng-show="import.seedSourceId == 'trezor' || import.seedSourceId == 'ledger'">
|
||||||
<label for="backupText">
|
|
||||||
<span translate>Paste the backup plain text code</span> <i class="fi-clipboard"></i>
|
<label class="oh"><span translate>Account Number</span>
|
||||||
|
<input type="number" id="account" ng-model="account">
|
||||||
</label>
|
</label>
|
||||||
<textarea class="form-control" name="backupText" ng-model="import.backupText" rows="5"></textarea>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label for="password"><span translate>Password</span>
|
<div ng-show="import.seedSourceId == 'trezor'">
|
||||||
</label>
|
<label for="isMultisig" class="oh">
|
||||||
<div class="input">
|
<span translate>Shared Wallet</span>
|
||||||
<input type="password" class="form-control" placeholder="{{'Your backup password'|translate}}"
|
<switch id="isMultisig" name="isMultisig" ng-model="isMultisig" class="green right m5t m10b"></switch>
|
||||||
name="password" ng-model="import.password">
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m10t oh" ng-init="hideAdv=true">
|
<div class="m10t oh" ng-init="hideAdv=true">
|
||||||
|
@ -152,100 +222,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button translate type="submit" class="button round expand black"
|
|
||||||
ng-disabled="importForm.$invalid || !import.password || import.loading">
|
|
||||||
Import backup
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="text-center text-gray p20v" ng-click="$root.go('importLegacy')">
|
|
||||||
<p class="text-gray m5b size-14" translate> Have a Backup from Copay v0.9?</p>
|
|
||||||
<button class=" outline dark-gray tiny round"> <span translate>Import here</span>
|
|
||||||
<i class="icon-arrow-right3 size-14"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row" ng-show="type == 'hwWallet'">
|
|
||||||
<div class="large-12 columns">
|
|
||||||
<div class="box-notification" ng-show="import.error">
|
|
||||||
<span class="text-warning size-14">
|
|
||||||
{{import.error|translate}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form name="importForm3" ng-submit="import.importLedger(importForm3)" ng-show="index.isChromeApp" novalidate>
|
|
||||||
<div class="large-12 columns">
|
|
||||||
|
|
||||||
<!-- TODO: account
|
|
||||||
<label class=" oh">
|
|
||||||
<span translate>Ledger Slot</span>
|
|
||||||
<select class="m10t" ng-model="externalIndex" ng-options="externalIndex as externalIndex for externalIndex in import.externalIndexValues">
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<div class="oh text-gray line-b size-12 p10b m20b"><span translate>Ledger supports up to 20 Copay wallets simultaneously. Select which slot to import</div>
|
|
||||||
-->
|
|
||||||
<button translate type="submit" class="button round expand black"
|
<button translate type="submit" class="button round expand black"
|
||||||
ng-disabled="import.loading || import.ledger">
|
ng-disabled="import.loading || import.ledger">
|
||||||
Import from Ledger
|
Import
|
||||||
</button>
|
</button>
|
||||||
|
</div> <!-- seedoptions show -->
|
||||||
<div class="m10t oh" ng-init="hideAdvLedger=true">
|
</div>
|
||||||
<a class="button outline light-gray expand tiny" ng-click="hideAdvLedger=!hideAdvLedger">
|
</form>
|
||||||
<i class="fi-widget m3r"></i>
|
|
||||||
<span translate ng-hide="!hideAdvLedger">Show advanced options</span>
|
|
||||||
<span translate ng-hide="hideAdvLedger">Hide advanced options</span>
|
|
||||||
<i ng-if="hideAdvLedger" class="icon-arrow-down4"></i>
|
|
||||||
<i ng-if="!hideAdvLedger" class="icon-arrow-up4"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div ng-hide="hideAdvLedger" class="row">
|
|
||||||
<div class="large-12 columns">
|
|
||||||
|
|
||||||
<label for="bws" class="oh">
|
|
||||||
<span>Wallet Service URL</span>
|
|
||||||
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<form name="importForm4" ng-submit="import.importTrezor(importForm4)" novalidate>
|
|
||||||
<div class="large-12 columns">
|
|
||||||
|
|
||||||
<div class="m10t oh" ng-init="hideAdvTrezor=true">
|
|
||||||
<a class="button outline light-gray expand tiny" ng-click="hideAdvTrezor=!hideAdvTrezor">
|
|
||||||
<i class="fi-widget m3r"></i>
|
|
||||||
<span translate ng-hide="!hideAdvTrezor">Show advanced options</span>
|
|
||||||
<span translate ng-hide="hideAdvTrezor">Hide advanced options</span>
|
|
||||||
<i ng-if="hideAdvTrezor" class="icon-arrow-down4"></i>
|
|
||||||
<i ng-if="!hideAdvTrezor" class="icon-arrow-up4"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div ng-hide="hideAdvTrezor" class="row">
|
|
||||||
<div class="large-12 columns">
|
|
||||||
|
|
||||||
<label for="bws" class="oh">
|
|
||||||
<span>Wallet Service URL</span>
|
|
||||||
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ng-disabled="import.loading || import.ledger" -->
|
|
||||||
<button translate type="submit" class="button round expand black" ng-disabled="true">
|
|
||||||
Import from TREZOR
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="extra-margin-bottom"></div>
|
<div class="extra-margin-bottom"></div>
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
<span ng-show="index.network != 'livenet'"> Testnet </span>
|
|
||||||
<span ng-show="!index.canSign && !index.isPrivKeyExternal" translate>No Private key</span>
|
<span ng-show="index.isShared"><span translate>{{index.m}}-of-{{index.n}}</span></span>
|
||||||
<div ng-show="index.isPrivKeyExternal" style="text-transform: capitalize">
|
<img style="height:1em" ng-show="index.network != 'livenet'" src="img/icon-testnet.svg">
|
||||||
<span translate>External Private Key:</span>
|
<img style="height:1em" ng-show="!index.canSign && !index.isPrivKeyExternal" src="img/icon-read-only.svg">
|
||||||
{{index.externalSource}}
|
|
||||||
</div>
|
<img style="height:1em" ng-show="index.externalSource == 'trezor'" src="img/icon-trezor.svg">
|
||||||
|
<img style="height:1em" ng-show="index.externalSource == 'ledger'" src="img/icon-ledger.svg">
|
||||||
|
<span style="height:1em" ng-show="index.account">#{{index.account || 0}} </span>
|
||||||
|
|
||||||
|
<img style="height:1em" ng-show="index.isPrivKeyEncrypted" src="img/icon-lock.svg">
|
||||||
|
|
||||||
|
<!-- <img style="height:1em" ng-show="index.preferences.email" src="img/icon-email.svg"> -->
|
||||||
|
<img style="height:1em" ng-show="index.usingCustomBWS" src="img/icon-bws.svg">
|
||||||
|
|
||||||
|
<img style="height:1em" class="animated flash infinite" ng-show="index.updatingTxHistory[index.walletId]" src="img/icon-sync.svg">
|
||||||
|
|
||||||
|
|
|
@ -32,118 +32,128 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<form name="joinForm" ng-submit="join.join(joinForm)" novalidate>
|
||||||
<div class="large-12 columns">
|
<div class="row">
|
||||||
|
<div class="large-12 columns">
|
||||||
|
<div class="box-notification" ng-show="join.error ">
|
||||||
|
<span class="text-warning size-14">
|
||||||
|
{{join.error|translate}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box-notification" ng-show="join.error ">
|
<div>
|
||||||
<span class="text-warning size-14">
|
|
||||||
{{join.error|translate}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form name="joinForm" ng-submit="join.join(joinForm)" novalidate>
|
|
||||||
<label><span translate>Your nickname</span>
|
<label><span translate>Your nickname</span>
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<input type="text" placeholder="{{'John'|translate}}" class="form-control" name="myName" ng-model="myName" ng-required="true">
|
<input type="text" placeholder="{{'John'|translate}}" class="form-control" name="myName" ng-model="myName" ng-required="true">
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row collapse">
|
<div class="row collapse">
|
||||||
<label for="secret" class="left"><span translate>Wallet Invitation</span>
|
<label for="secret" class="left"><span translate>Wallet Invitation</span>
|
||||||
<small translate ng-show="joinForm.secret.$pristine">Required</small>
|
<small translate ng-show="joinForm.secret.$pristine">Required</small>
|
||||||
</label>
|
</label>
|
||||||
<span class="has-error right size-12" ng-show="joinForm.secret.$invalid
|
<span class="has-error right size-12" ng-show="joinForm.secret.$invalid
|
||||||
&& !joinForm.secret.$pristine">
|
&& !joinForm.secret.$pristine">
|
||||||
<span class="icon-input"><i class="fi-x"></i></span>
|
<span class="icon-input"><i class="fi-x"></i></span>
|
||||||
<span translate>Wallet Invitation is not valid!</span>
|
<span translate>Wallet Invitation is not valid!</span>
|
||||||
</span>
|
</span>
|
||||||
<small class="icon-input right" ng-show="joinForm.secret.$valid
|
<small class="icon-input right" ng-show="joinForm.secret.$valid
|
||||||
&& !joinForm.secret.$pristine"><i class="fi-check"></i></small>
|
&& !joinForm.secret.$pristine"><i class="fi-check"></i></small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input">
|
||||||
|
<input id="secret" type="text" placeholder="{{'Paste invitation here'|translate}}" name="secret" ng-model="secret" wallet-secret required>
|
||||||
|
<div class="qr-scanner-input">
|
||||||
|
<qr-scanner on-scan="join.onQrCodeScanned(data)"></qr-scanner>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input">
|
<div class="m10t oh" ng-init="hideAdv=true">
|
||||||
<input id="secret" type="text" placeholder="{{'Paste invitation here'|translate}}" name="secret" ng-model="secret" wallet-secret required>
|
<a class="button outline light-gray expand tiny" ng-click="hideAdv=!hideAdv">
|
||||||
<div class="qr-scanner-input">
|
|
||||||
<qr-scanner on-scan="join.onQrCodeScanned(data)"></qr-scanner>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a class="button outline light-gray tiny expand" ng-click="join.hideAdv=!join.hideAdv">
|
|
||||||
<i class="fi-widget m3r"></i>
|
<i class="fi-widget m3r"></i>
|
||||||
<span translate ng-show="!join.hideAdv">Show advanced options</span>
|
<span translate ng-hide="!hideAdv">Show advanced options</span>
|
||||||
<span translate ng-show="join.hideAdv">Hide advanced options</span>
|
<span translate ng-hide="hideAdv">Hide advanced options</span>
|
||||||
<i ng-show="!join.hideAdv" class="icon-arrow-down4"></i>
|
<i ng-if="hideAdv" class="icon-arrow-down4"></i>
|
||||||
<i ng-show="join.hideAdv" class="icon-arrow-up4"></i>
|
<i ng-if="!hideAdv" class="icon-arrow-up4"></i>
|
||||||
</a>
|
</a>
|
||||||
<div ng-show="join.hideAdv" class="row">
|
</div>
|
||||||
<div class="large-12 columns">
|
|
||||||
|
|
||||||
<label for="hw-ledger" class="oh" ng-show="index.isChromeApp">
|
<div ng-hide="hideAdv" class="row">
|
||||||
<span translate>Use Ledger hardware wallet</span>
|
<div class="large-12 columns">
|
||||||
<switch id="hw-ledger" name="hwLedger" ng-model="hwLedger" class="green right m5t m10b"></switch>
|
<div>
|
||||||
</label>
|
<label for="bws" class="oh">
|
||||||
|
<span>Wallet Service URL</span>
|
||||||
|
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<label ng-show="!index.isCordova && 0" for="hw-trezor" class="oh">
|
<div>
|
||||||
<span translate>Use TREZOR hardware wallet</span>
|
<label><span translate>Wallet Seed</span>
|
||||||
<switch id="hw-trezor" name="hwTrezor" ng-model="hwTrezor" class="green right m5t m10b"></switch>
|
<select class="m10t" ng-model="seedSource"
|
||||||
</label>
|
ng-options="seed as seed.label for seed in join.seedOptions"
|
||||||
|
ng-change="join.setSeedSource()">
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- TODO account
|
<div ng-show="join.seedSourceId == 'trezor' || join.seedSourceId == 'ledger'">
|
||||||
<div class="large-12 columns" ng-hide="!hwLedger">
|
|
||||||
<label class="oh">
|
<label class="oh"><span translate>Account</span>
|
||||||
<span translate>Ledger Slot</span>
|
<select class="m10t" ng-model="account" ng-options="externalIndex as externalIndex for externalIndex in join.accountValues">
|
||||||
<select class="m10t" ng-model="externalIndex" ng-options="externalIndex as externalIndex for externalIndex in join.externalIndexValues">
|
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<div class="oh text-gray line-b size-12 p10b m20b"><span translate>Ledger supports up to 20 Copay wallets simultaneously. Select which slot should be used to host this wallet</div>
|
<div class="oh text-gray line-b size-12 p10b m20b"><span translate>Multiple wallets accounts are supported on the device simultaneously. Select which account should be used for this wallet</div>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
<label ng-show="!hwLedger && !hwTrezor" for="seed" class="oh">
|
|
||||||
<span translate>Specify your wallet seed</span>
|
|
||||||
<switch id="seed" name="setSeed" ng-model="setSeed" class="green right m5t m10b"></switch>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="createPassphrase" class="oh" ng-show="!setSeed && !hwLedger && !hwTrezor" ><span translate>Add a Seed Passphrase</span> <small translate>Add an optional passphrase to secure the seed</small>
|
<div class="box-notification" ng-show="join.seedSourceId=='new' && createPassphrase">
|
||||||
<div class="input">
|
<span class="text-warning size-14">
|
||||||
<input type="text" class="form-control"
|
<i class="fi-alert"></i>
|
||||||
name="createPassphrase" ng-model="createPassphrase">
|
<span translate>
|
||||||
</div>
|
WARNING: Passphrase cannot be recovered. <b>Be sure to write it down</b>. The wallet can not be restored without the passphrase.
|
||||||
</label>
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<label for="ext-master" class="m10t" ng-show="setSeed">
|
<div ng-show="join.seedSourceId=='new' ">
|
||||||
<span translate>Wallet Seed</span>
|
<label for="createPassphrase" ><span translate>Add a Seed Passphrase</span> <small translate>Add an optional passphrase to secure the seed</small>
|
||||||
<small translate>Enter the seed words (BIP39)</small>
|
<div class="input">
|
||||||
<input id="ext-master"
|
<input type="text" class="form-control"
|
||||||
type="text"
|
name="createPassphrase" ng-model="createPassphrase">
|
||||||
name="privateKey" ng-model="privateKey">
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label for="passphrase" class="oh" ng-show="setSeed"><span translate>Seed Passphrase</span> <small translate>The seed could require a passphrase to be imported</small>
|
</div>
|
||||||
<div class="input">
|
|
||||||
<input type="text" class="form-control" name="passphrase" ng-model="passphrase">
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="bws" class="oh">
|
<div ng-show="join.seedSourceId=='set'">
|
||||||
<span>Wallet Service URL</span>
|
<label for="ext-master">
|
||||||
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
|
<span translate>Wallet Seed</span>
|
||||||
</label>
|
<small translate>Enter the seed words (BIP39)</small>
|
||||||
</div>
|
<input id="ext-master"
|
||||||
</div>
|
type="text"
|
||||||
|
name="privateKey" ng-model="privateKey">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box-notification" ng-show="!setSeed && createPassphrase">
|
<div ng-show="join.seedSourceId=='set'">
|
||||||
<span class="text-warning size-14">
|
<label for="passphrase"> <span translate>Seed Passphrase</span> <small translate>The seed could require a passphrase to be imported</small>
|
||||||
<i class="fi-alert"></i>
|
<div class="input">
|
||||||
<span translate>
|
<input type="text" class="form-control" name="passphrase" ng-model="passphrase">
|
||||||
WARNING: Passphrase cannot be recovered. <b>Be sure to write it down</b>. The wallet can not be restored without the passphrase.
|
</div>
|
||||||
</span>
|
</label>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
<div ng-show="join.seedSourceId == 'set'">
|
||||||
|
<label class="oh"><span translate>Derivation Path</span> <small translate>BIP32 path for address derivation</small>
|
||||||
|
<input type="text" class="form-control" name="derivationPath" ng-model="derivationPath">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div> <!-- columns -->
|
||||||
|
</div> <!-- advanced -->
|
||||||
|
|
||||||
<button translate type="submit" class="button expand black m0 round"
|
<button translate type="submit" class="button expand black m0 round"
|
||||||
ng-disabled="joinForm.$invalid || join.loading">Join</button>
|
ng-disabled="joinForm.$invalid || join.loading">Join</button>
|
||||||
</form>
|
</div> <!-- large-12 columns -->
|
||||||
</div>
|
</div> <!-- row -->
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="extra-margin-bottom"></div>
|
<div class="extra-margin-bottom"></div>
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
<nav class="tab-bar">
|
|
||||||
<section class="left-small">
|
|
||||||
<a ng-click="cancel()" class="p10">
|
|
||||||
<span class="text-close" translate>Close</span>
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="middle tab-bar-section">
|
|
||||||
<h1 class="title ellipsis" ng-style="{'color':color}" translate>
|
|
||||||
Copayers
|
|
||||||
</h1>
|
|
||||||
</section>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="modal-content fix-modals-touch">
|
|
||||||
<ul class="no-bullet">
|
|
||||||
<li class="line-b p10" ng-repeat="copayer in copayers">
|
|
||||||
<span class="size-12" ng-show="copayer.id == copayerId">
|
|
||||||
<i class="icon-contact size-24 m10r"></i> {{copayer.name}} ({{'Me'|translate}}) <i class="fi-check m5 right"></i>
|
|
||||||
</span>
|
|
||||||
<span class="size-12 text-gray" ng-show="copayer.id != copayerId">
|
|
||||||
<i class="icon-contact size-24 m10r"></i> {{copayer.name}}<i class="fi-check m5 right"></i>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="extra-margin-bottom"></div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -62,12 +62,12 @@
|
||||||
<div translate>Advanced</div>
|
<div translate>Advanced</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div ng-show="!index.noFocusedWallet && (!index.isPrivKeyExternal || preferences.touchidAvailable)">
|
<div ng-show="!index.noFocusedWallet && index.canSign">
|
||||||
<h4 translate>
|
<h4 translate ng-show="index.canSign">
|
||||||
Spending Restrictions
|
Spending Restrictions
|
||||||
</h4>
|
</h4>
|
||||||
<ul class="no-bullet m0 ">
|
<ul class="no-bullet m0">
|
||||||
<li ng-show="!index.isPrivKeyExternal">
|
<li>
|
||||||
<switch id="network-name" name="encrypt" ng-model="encrypt" class="green right"></switch>
|
<switch id="network-name" name="encrypt" ng-model="encrypt" class="green right"></switch>
|
||||||
<div translate>Request Password</div>
|
<div translate>Request Password</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
<li class="line-b p20 oh">
|
<li class="line-b p20 oh">
|
||||||
<span translate>Wallet Id</span>
|
<span translate>Wallet Id</span>
|
||||||
<span class="right text-gray enable_text_select">
|
<span class="right text-gray enable_text_select">
|
||||||
|
@ -29,6 +30,8 @@
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<li class="line-b p20 oh">
|
<li class="line-b p20 oh">
|
||||||
<span translate>Wallet Network</span>
|
<span translate>Wallet Network</span>
|
||||||
<span class="right text-gray">
|
<span class="right text-gray">
|
||||||
|
@ -52,6 +55,39 @@
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="line-b p20 oh" ng-show="index.externalSource">
|
||||||
|
<span translate>Hardware Wallet</span>
|
||||||
|
<span class="right text-gray capitalize">
|
||||||
|
{{index.externalSource}}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="line-b p20 oh" ng-show="!index.externalSource && !index.canSign">
|
||||||
|
<span translate></span>
|
||||||
|
<span class="right text-gray capitalize">
|
||||||
|
No private key
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="line-b p20 oh" ng-show="index.account">
|
||||||
|
<span translate>Account ({{derivationStrategy}})</span>
|
||||||
|
<span class="right text-gray">
|
||||||
|
#{{index.account}}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="title m0" translate>Copayers</h4>
|
||||||
|
<li ng-repeat="copayer in index.copayers">
|
||||||
|
<span class="size-12" ng-show="copayer.id == index.copayerId">
|
||||||
|
<i class="icon-contact size-24 m10r"></i> {{copayer.name}} ({{'Me'|translate}}) <i class="fi-check m5 right"></i>
|
||||||
|
</span>
|
||||||
|
<span class="size-12 text-gray" ng-show="copayer.id != index.copayerId">
|
||||||
|
<i class="icon-contact size-24 m10r"></i> {{copayer.name}}<i class="fi-check m5 right"></i>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
<h4 class="title m0" translate>Extended Public Keys</h4>
|
<h4 class="title m0" translate>Extended Public Keys</h4>
|
||||||
<li ng-repeat="pk in pubKeys">
|
<li ng-repeat="pk in pubKeys">
|
||||||
<div class="row collapse">
|
<div class="row collapse">
|
||||||
|
|
|
@ -118,13 +118,11 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="wallet-info">
|
<div class="wallet-info">
|
||||||
<div ng-show="index.isShared" ng-click="openCopayersModal(index.copayers, index.copayerId)">
|
<div ng-show="index.isShared">
|
||||||
<p class="m0">
|
<p class="m0">
|
||||||
{{(index.alias || index.walletName)}}
|
{{(index.alias || index.walletName)}}
|
||||||
</p>
|
</p>
|
||||||
<div class="size-12 text-gray">
|
<div class="size-12 text-gray">
|
||||||
<span translate>Multisignature wallet</span>
|
|
||||||
(<span translate>{{index.m}}-of-{{index.n}}</span>)
|
|
||||||
<span ng-include="'views/includes/walletInfo.html'"></span>
|
<span ng-include="'views/includes/walletInfo.html'"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -444,7 +442,7 @@
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<div id="history" class="history tab-view">
|
<div id="history" class="history tab-view">
|
||||||
<div class="row m20t" ng-show="!index.txHistory[0] && !index.updatingTxHistory">
|
<div class="row m20t" ng-show="!index.txHistory[0] && !index.updatingTxHistory[index.walletId]">
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<div class="oh text-center">
|
<div class="oh text-center">
|
||||||
<span ng-show="index.txHistoryError && !index.notAuthorized" ng-click='index.updateTxHistory()'>
|
<span ng-show="index.txHistoryError && !index.notAuthorized" ng-click='index.updateTxHistory()'>
|
||||||
|
@ -457,7 +455,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row m20t text-center" ng-show="index.updatingTxHistory">
|
<div class="row m20t text-center" ng-show="index.updatingTxHistory[index.walletId]">
|
||||||
<div class="columns large-12 medium-12 small-12">
|
<div class="columns large-12 medium-12 small-12">
|
||||||
<div class="spinner">
|
<div class="spinner">
|
||||||
<div class="rect1"></div>
|
<div class="rect1"></div>
|
||||||
|
@ -467,18 +465,16 @@
|
||||||
<div class="rect5"></div>
|
<div class="rect5"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p ng-show="index.showWaitingSign" translate class="size-12 text-gray">
|
<div ng-show="index.txProgress > 6" translate class="size-12 text-gray m20t">
|
||||||
Initial transaction history synchronization can take some minutes for wallets with many transactions.</br>
|
Initial transaction history synchronization can take some minutes for wallets with many transactions.</br>
|
||||||
Please stand by.
|
Please stand by.
|
||||||
</p>
|
</div>
|
||||||
</div>
|
<div ng-show="index.txProgress > 6" translate class="size-14 text-gray m20t">
|
||||||
<div ng-if="!index.isCordova && index.txHistory[0] && !index.updatingTxHistory" class="m20t text-center">
|
<b>{{index.txProgress}}</b> Transactions<br>
|
||||||
<input id="export_file" type="file" nwsaveas="Copay-{{index.alias || index.walletName}}.csv" accept=".csv" style="display:none">
|
Downloaded
|
||||||
<a class="text-gray size-12" ng-click="index.csvHistory();">
|
</div>
|
||||||
<i class="fi-page-export-csv"></i>
|
|
||||||
<span translate>Download CSV file</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-show="index.txHistory[0]">
|
<div ng-show="index.txHistory[0]">
|
||||||
<div ng-repeat="btx in index.txHistory"
|
<div ng-repeat="btx in index.txHistory"
|
||||||
ng-click="home.openTxModal(btx)"
|
ng-click="home.openTxModal(btx)"
|
||||||
|
@ -522,6 +518,33 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row m20t text-center" ng-show="index.historyRendering">
|
||||||
|
<div class="columns large-12 medium-12 small-12">
|
||||||
|
<div class="spinner">
|
||||||
|
<div class="rect1"></div>
|
||||||
|
<div class="rect2"></div>
|
||||||
|
<div class="rect3"></div>
|
||||||
|
<div class="rect4"></div>
|
||||||
|
<div class="rect5"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row m20t">
|
||||||
|
<div class="large-6 medium-6 small-6 columns">
|
||||||
|
<button type="submit" class="button black round expand" ng-show="index.historyShowShowAll" ng-click="index.showAllHistory()" ng-style="{'background-color':index.backgroundColor}" translate>
|
||||||
|
<span translate>Show All</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="large-6 medium-6 small-6 columns" ng-show="!index.isCordova">
|
||||||
|
<input id="export_file" type="file" nwsaveas="Copay-{{index.alias || index.walletName}}.csv" accept=".csv" style="display:none">
|
||||||
|
<a class="button outline dark-gray round" ng-click="index.csvHistory();">
|
||||||
|
<i class="fi-page-export-csv"></i>
|
||||||
|
<span translate>Download CSV file</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="extra-margin-bottom"></div>
|
<div class="extra-margin-bottom"></div>
|
||||||
</div> <!-- END History -->
|
</div> <!-- END History -->
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('createController',
|
angular.module('copayApp.controllers').controller('createController',
|
||||||
function($scope, $rootScope, $location, $timeout, $log, lodash, go, profileService, configService, isCordova, gettext, ledger, trezor, isMobile) {
|
function($scope, $rootScope, $location, $timeout, $log, lodash, go, profileService, configService, isCordova, gettext, ledger, trezor, isMobile, isChromeApp, isDevel, derivationPathHelper) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var defaults = configService.getDefaults();
|
var defaults = configService.getDefaults();
|
||||||
this.isWindowsPhoneApp = isMobile.Windows() && isCordova;
|
this.isWindowsPhoneApp = isMobile.Windows() && isCordova;
|
||||||
|
$scope.account = 1;
|
||||||
|
|
||||||
/* For compressed keys, m*73 + n*34 <= 496 */
|
/* For compressed keys, m*73 + n*34 <= 496 */
|
||||||
var COPAYER_PAIR_LIMITS = {
|
var COPAYER_PAIR_LIMITS = {
|
||||||
|
@ -25,6 +26,7 @@ angular.module('copayApp.controllers').controller('createController',
|
||||||
|
|
||||||
var defaults = configService.getDefaults();
|
var defaults = configService.getDefaults();
|
||||||
$scope.bwsurl = defaults.bws.url;
|
$scope.bwsurl = defaults.bws.url;
|
||||||
|
$scope.derivationPath = derivationPathHelper.default;
|
||||||
|
|
||||||
// ng-repeat defined number of times instead of repeating over array?
|
// ng-repeat defined number of times instead of repeating over array?
|
||||||
this.getNumber = function(num) {
|
this.getNumber = function(num) {
|
||||||
|
@ -38,11 +40,47 @@ angular.module('copayApp.controllers').controller('createController',
|
||||||
$scope.requiredCopayers = Math.min(parseInt(n / 2 + 1), maxReq);
|
$scope.requiredCopayers = Math.min(parseInt(n / 2 + 1), maxReq);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var updateSeedSourceSelect = function(n) {
|
||||||
|
|
||||||
|
self.seedOptions = [{
|
||||||
|
id: 'new',
|
||||||
|
label: gettext('New Random Seed'),
|
||||||
|
}, {
|
||||||
|
id: 'set',
|
||||||
|
label: gettext('Specify Seed...'),
|
||||||
|
}];
|
||||||
|
$scope.seedSource = self.seedOptions[0];
|
||||||
|
|
||||||
|
if (n > 1 && isChromeApp)
|
||||||
|
self.seedOptions.push({
|
||||||
|
id: 'ledger',
|
||||||
|
label: gettext('Ledger Hardware Wallet'),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isChromeApp || isDevel) {
|
||||||
|
self.seedOptions.push({
|
||||||
|
id: 'trezor',
|
||||||
|
label: gettext('Trezor Hardware Wallet'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.TCValues = lodash.range(2, defaults.limits.totalCopayers + 1);
|
this.TCValues = lodash.range(2, defaults.limits.totalCopayers + 1);
|
||||||
$scope.totalCopayers = defaults.wallet.totalCopayers;
|
$scope.totalCopayers = defaults.wallet.totalCopayers;
|
||||||
|
|
||||||
this.setTotalCopayers = function(tc) {
|
this.setTotalCopayers = function(tc) {
|
||||||
updateRCSelect(tc);
|
updateRCSelect(tc);
|
||||||
|
updateSeedSourceSelect(tc);
|
||||||
|
self.seedSourceId = $scope.seedSource.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.setSeedSource = function(src) {
|
||||||
|
self.seedSourceId = $scope.seedSource.id;
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
$rootScope.$apply();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.create = function(form) {
|
this.create = function(form) {
|
||||||
|
@ -50,23 +88,36 @@ angular.module('copayApp.controllers').controller('createController',
|
||||||
this.error = gettext('Please enter the required fields');
|
this.error = gettext('Please enter the required fields');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var opts = {
|
var opts = {
|
||||||
m: $scope.requiredCopayers,
|
m: $scope.requiredCopayers,
|
||||||
n: $scope.totalCopayers,
|
n: $scope.totalCopayers,
|
||||||
name: form.walletName.$modelValue,
|
name: form.walletName.$modelValue,
|
||||||
myName: $scope.totalCopayers > 1 ? form.myName.$modelValue : null,
|
myName: $scope.totalCopayers > 1 ? form.myName.$modelValue : null,
|
||||||
networkName: form.isTestnet.$modelValue ? 'testnet' : 'livenet',
|
networkName: form.isTestnet.$modelValue ? 'testnet' : 'livenet',
|
||||||
bwsurl: $scope.bwsurl
|
bwsurl: $scope.bwsurl,
|
||||||
};
|
};
|
||||||
var setSeed = form.setSeed.$modelValue;
|
var setSeed = self.seedSourceId == 'set';
|
||||||
if (setSeed) {
|
if (setSeed) {
|
||||||
var words = form.privateKey.$modelValue;
|
|
||||||
|
var words = form.privateKey.$modelValue || '';
|
||||||
if (words.indexOf(' ') == -1 && words.indexOf('prv') == 1 && words.length > 108) {
|
if (words.indexOf(' ') == -1 && words.indexOf('prv') == 1 && words.length > 108) {
|
||||||
opts.extendedPrivateKey = words;
|
opts.extendedPrivateKey = words;
|
||||||
} else {
|
} else {
|
||||||
opts.mnemonic = words;
|
opts.mnemonic = words;
|
||||||
}
|
}
|
||||||
opts.passphrase = form.passphrase.$modelValue;
|
opts.passphrase = form.passphrase.$modelValue;
|
||||||
|
|
||||||
|
var pathData = derivationPathHelper.parse($scope.derivationPath);
|
||||||
|
if (!pathData) {
|
||||||
|
this.error = gettext('Invalid derivation path');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.account = pathData.account;
|
||||||
|
opts.networkName = pathData.networkName;
|
||||||
|
opts.derivationStrategy = pathData.derivationStrategy;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
opts.passphrase = form.createPassphrase.$modelValue;
|
opts.passphrase = form.createPassphrase.$modelValue;
|
||||||
}
|
}
|
||||||
|
@ -76,14 +127,21 @@ angular.module('copayApp.controllers').controller('createController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (form.hwLedger.$modelValue || form.hwTrezor.$modelValue) {
|
if (self.seedSourceId == 'ledger' || self.seedSourceId == 'trezor') {
|
||||||
self.hwWallet = form.hwLedger.$modelValue ? 'Ledger' : 'TREZOR';
|
var account = $scope.account;
|
||||||
|
if (!account || account < 1) {
|
||||||
|
this.error = gettext('Invalid account number');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var src = form.hwLedger.$modelValue ? ledger : trezor;
|
if ( self.seedSourceId == 'trezor')
|
||||||
|
account = account - 1;
|
||||||
|
|
||||||
// TODO : account
|
opts.account = account;
|
||||||
var account = 0;
|
self.hwWallet = self.seedSourceId == 'ledger' ? 'Ledger' : 'Trezor';
|
||||||
src.getInfoForNewWallet(account, function(err, lopts) {
|
var src = self.seedSourceId == 'ledger' ? ledger : trezor;
|
||||||
|
|
||||||
|
src.getInfoForNewWallet(opts.n > 1, account, function(err, lopts) {
|
||||||
self.hwWallet = false;
|
self.hwWallet = false;
|
||||||
if (err) {
|
if (err) {
|
||||||
self.error = err;
|
self.error = err;
|
||||||
|
@ -141,4 +199,7 @@ angular.module('copayApp.controllers').controller('createController',
|
||||||
$scope.$on("$destroy", function() {
|
$scope.$on("$destroy", function() {
|
||||||
$rootScope.hideWalletNavigation = false;
|
$rootScope.hideWalletNavigation = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateSeedSourceSelect(1);
|
||||||
|
self.setSeedSource('new');
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('importController',
|
angular.module('copayApp.controllers').controller('importController',
|
||||||
function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor) {
|
function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, isChromeApp, isDevel, derivationPathHelper) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
var defaults = configService.getDefaults();
|
var defaults = configService.getDefaults();
|
||||||
$scope.bwsurl = defaults.bws.url;
|
$scope.bwsurl = defaults.bws.url;
|
||||||
|
$scope.derivationPath = derivationPathHelper.default;
|
||||||
|
$scope.account = 1;
|
||||||
|
|
||||||
window.ignoreMobilePause = true;
|
window.ignoreMobilePause = true;
|
||||||
$scope.$on('$destroy', function() {
|
$scope.$on('$destroy', function() {
|
||||||
|
@ -15,6 +17,27 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var updateSeedSourceSelect = function() {
|
||||||
|
self.seedOptions = [];
|
||||||
|
|
||||||
|
if (isChromeApp) {
|
||||||
|
self.seedOptions.push({
|
||||||
|
id: 'ledger',
|
||||||
|
label: gettext('Ledger Hardware Wallet'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isChromeApp || isDevel) {
|
||||||
|
self.seedOptions.push({
|
||||||
|
id: 'trezor',
|
||||||
|
label: gettext('Trezor Hardware Wallet'),
|
||||||
|
});
|
||||||
|
$scope.seedSource = self.seedOptions[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.setType = function(type) {
|
this.setType = function(type) {
|
||||||
$scope.type = type;
|
$scope.type = type;
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
@ -173,23 +196,23 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.passphrase = form.passphrase.$modelValue || null;
|
opts.passphrase = form.passphrase.$modelValue || null;
|
||||||
opts.networkName = form.isTestnet.$modelValue ? 'testnet' : 'livenet';
|
|
||||||
|
var pathData = derivationPathHelper.parse($scope.derivationPath);
|
||||||
|
if (!pathData) {
|
||||||
|
this.error = gettext('Invalid derivation path');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
opts.account = pathData.account;
|
||||||
|
opts.networkName = pathData.networkName;
|
||||||
|
opts.derivationStrategy = pathData.derivationStrategy;
|
||||||
|
|
||||||
|
|
||||||
_importMnemonic(words, opts);
|
_importMnemonic(words, opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.importTrezor = function(form) {
|
this.importTrezor = function(account, isMultisig) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (form.$invalid) {
|
trezor.getInfoForNewWallet(isMultisig, account, function(err, lopts) {
|
||||||
this.error = gettext('There is an error in the form');
|
|
||||||
$timeout(function() {
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.hwWallet = 'Trezor';
|
|
||||||
// TODO account
|
|
||||||
trezor.getInfoForNewWallet(0, function(err, lopts) {
|
|
||||||
self.hwWallet = false;
|
self.hwWallet = false;
|
||||||
if (err) {
|
if (err) {
|
||||||
self.error = err;
|
self.error = err;
|
||||||
|
@ -217,18 +240,53 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.importLedger = function(form) {
|
this.importHW = function(form) {
|
||||||
var self = this;
|
if (form.$invalid || $scope.account < 0 ) {
|
||||||
if (form.$invalid) {
|
|
||||||
this.error = gettext('There is an error in the form');
|
this.error = gettext('There is an error in the form');
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.hwWallet = 'Ledger';
|
this.error = '';
|
||||||
// TODO account
|
|
||||||
ledger.getInfoForNewWallet(0, function(err, lopts) {
|
var account = + $scope.account;
|
||||||
|
|
||||||
|
if (self.seedSourceId == 'trezor') {
|
||||||
|
if ( account < 1) {
|
||||||
|
this.error = gettext('Invalid account number');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
account = account - 1;
|
||||||
|
}
|
||||||
|
var isMultisig = form.isMultisig.$modelValue;
|
||||||
|
|
||||||
|
switch (self.seedSourceId) {
|
||||||
|
case ('ledger'):
|
||||||
|
self.hwWallet = 'Ledger';
|
||||||
|
self.importLedger(account);
|
||||||
|
break;
|
||||||
|
case ('trezor'):
|
||||||
|
self.hwWallet = 'Trezor';
|
||||||
|
self.importTrezor(account, isMultisig);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw ('Error: bad source id');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setSeedSource = function() {
|
||||||
|
if (!$scope.seedSource) return;
|
||||||
|
self.seedSourceId = $scope.seedSource.id;
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
$rootScope.$apply();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.importLedger = function(account) {
|
||||||
|
var self = this;
|
||||||
|
ledger.getInfoForNewWallet(true, account, function(err, lopts) {
|
||||||
self.hwWallet = false;
|
self.hwWallet = false;
|
||||||
if (err) {
|
if (err) {
|
||||||
self.error = err;
|
self.error = err;
|
||||||
|
@ -255,4 +313,6 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updateSeedSourceSelect();
|
||||||
|
self.setSeedSource('new');
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,8 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
self.isChromeApp = isChromeApp;
|
self.isChromeApp = isChromeApp;
|
||||||
self.isSafari = isMobile.Safari();
|
self.isSafari = isMobile.Safari();
|
||||||
self.onGoingProcess = {};
|
self.onGoingProcess = {};
|
||||||
self.limitHistory = 6;
|
self.historyShowLimit = 10;
|
||||||
|
self.updatingTxHistory = {};
|
||||||
|
|
||||||
function strip(number) {
|
function strip(number) {
|
||||||
return (parseFloat(number.toPrecision(12)));
|
return (parseFloat(number.toPrecision(12)));
|
||||||
|
@ -83,7 +84,9 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
self.currentFeeLevel = null;
|
self.currentFeeLevel = null;
|
||||||
self.notAuthorized = false;
|
self.notAuthorized = false;
|
||||||
self.txHistory = [];
|
self.txHistory = [];
|
||||||
self.txHistoryUnique = {};
|
self.completeHistory = [];
|
||||||
|
self.txProgress = 0;
|
||||||
|
self.historyShowShowAll = false;
|
||||||
self.balanceByAddress = null;
|
self.balanceByAddress = null;
|
||||||
self.pendingTxProposalsCountForUs = null;
|
self.pendingTxProposalsCountForUs = null;
|
||||||
self.setSpendUnconfirmed();
|
self.setSpendUnconfirmed();
|
||||||
|
@ -106,7 +109,13 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
self.isComplete = fc.isComplete();
|
self.isComplete = fc.isComplete();
|
||||||
self.canSign = fc.canSign();
|
self.canSign = fc.canSign();
|
||||||
self.isPrivKeyExternal = fc.isPrivKeyExternal();
|
self.isPrivKeyExternal = fc.isPrivKeyExternal();
|
||||||
|
self.isPrivKeyEncrypted = fc.isPrivKeyEncrypted();
|
||||||
self.externalSource = fc.getPrivKeyExternalSourceName();
|
self.externalSource = fc.getPrivKeyExternalSourceName();
|
||||||
|
self.account = fc.credentials.account;
|
||||||
|
|
||||||
|
if (self.externalSource == 'trezor')
|
||||||
|
self.account++;
|
||||||
|
|
||||||
self.txps = [];
|
self.txps = [];
|
||||||
self.copayers = [];
|
self.copayers = [];
|
||||||
self.updateColor();
|
self.updateColor();
|
||||||
|
@ -115,6 +124,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
|
|
||||||
self.initGlidera();
|
self.initGlidera();
|
||||||
|
|
||||||
|
self.setCustomBWSFlag();
|
||||||
if (fc.isPrivKeyExternal()) {
|
if (fc.isPrivKeyExternal()) {
|
||||||
self.needsBackup = false;
|
self.needsBackup = false;
|
||||||
self.openWallet();
|
self.openWallet();
|
||||||
|
@ -127,6 +137,13 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.setCustomBWSFlag = function() {
|
||||||
|
var defaults = configService.getDefaults();
|
||||||
|
var config = configService.getSync();
|
||||||
|
|
||||||
|
self.usingCustomBWS = config.bwsFor && (config.bwsFor[self.walletId] != defaults.bws.url);
|
||||||
|
};
|
||||||
|
|
||||||
self.setTab = function(tab, reset, tries, switchState) {
|
self.setTab = function(tab, reset, tries, switchState) {
|
||||||
tries = tries || 0;
|
tries = tries || 0;
|
||||||
|
|
||||||
|
@ -485,12 +502,13 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
|
|
||||||
var SAFE_CONFIRMATIONS = 6;
|
var SAFE_CONFIRMATIONS = 6;
|
||||||
|
|
||||||
self.setTxHistory = function(txs) {
|
self.processNewTxs = function(txs) {
|
||||||
var config = configService.getSync().wallet.settings;
|
var config = configService.getSync().wallet.settings;
|
||||||
var now = Math.floor(Date.now() / 1000);
|
var now = Math.floor(Date.now() / 1000);
|
||||||
self.txHistoryUnique = {};
|
var txHistoryUnique = {};
|
||||||
|
var ret = [];
|
||||||
self.hasUnsafeConfirmed = false;
|
self.hasUnsafeConfirmed = false;
|
||||||
|
|
||||||
lodash.each(txs, function(tx) {
|
lodash.each(txs, function(tx) {
|
||||||
tx = txFormatService.processTx(tx);
|
tx = txFormatService.processTx(tx);
|
||||||
|
|
||||||
|
@ -505,13 +523,15 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
self.hasUnsafeConfirmed = true;
|
self.hasUnsafeConfirmed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!self.txHistoryUnique[tx.txid]) {
|
if (!txHistoryUnique[tx.txid]) {
|
||||||
self.txHistory.push(tx);
|
ret.push(tx);
|
||||||
self.txHistoryUnique[tx.txid] = true;
|
txHistoryUnique[tx.txid] = true;
|
||||||
} else {
|
} else {
|
||||||
$log.debug('Ignoring duplicate TX in history: ' + tx.txid)
|
$log.debug('Ignoring duplicate TX in history: ' + tx.txid)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.updateAlias = function() {
|
self.updateAlias = function() {
|
||||||
|
@ -735,13 +755,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
self.stopSync = function(remoteTx, localTx) {
|
|
||||||
if (remoteTx.txid == localTx.txid)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.removeSoftConfirmedTx = function(txs) {
|
self.removeSoftConfirmedTx = function(txs) {
|
||||||
return lodash.map(txs, function(tx) {
|
return lodash.map(txs, function(tx) {
|
||||||
if (tx.confirmations >= SOFT_CONFIRMATION_LIMIT)
|
if (tx.confirmations >= SOFT_CONFIRMATION_LIMIT)
|
||||||
|
@ -749,17 +762,14 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.getConfirmedTxs = function(cb) {
|
self.getConfirmedTxs = function(walletId, cb) {
|
||||||
var fc = profileService.focusedClient;
|
|
||||||
var c = fc.credentials;
|
|
||||||
|
|
||||||
storageService.getTxHistory(c.walletId, function(err, txs) {
|
storageService.getTxHistory(walletId, function(err, txs) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
var localTxs = [];
|
var localTxs = [];
|
||||||
|
|
||||||
if (!txs) {
|
if (!txs) {
|
||||||
self.showWaitingSign = true;
|
|
||||||
return cb(null, localTxs);
|
return cb(null, localTxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,72 +782,107 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateLocalTxHistory = function(cb) {
|
self.updateLocalTxHistory = function(client, cb) {
|
||||||
self.getConfirmedTxs(function(err, txsFromLocal) {
|
var requestLimit = 6;
|
||||||
|
var walletId = client.credentials.walletId;
|
||||||
|
|
||||||
|
self.getConfirmedTxs(walletId, function(err, txsFromLocal) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
var endingTxid = txsFromLocal[0] ? txsFromLocal[0].txid : null;
|
||||||
|
|
||||||
var fc = profileService.focusedClient;
|
function getNewTxs(newTxs, skip, i_cb) {
|
||||||
var c = fc.credentials;
|
|
||||||
fillTxsObject();
|
|
||||||
|
|
||||||
function fillTxsObject(txsResult, index) {
|
self.getTxsFromServer(client, skip, endingTxid, requestLimit, function(err, res, shouldContinue) {
|
||||||
txsResult = txsResult || [];
|
if (err) return i_cb(err);
|
||||||
index = index || 0;
|
|
||||||
|
|
||||||
self.makeTxHistoryRequest(txsResult, index, txsFromLocal[0], function(err, newIndex, exitLoop) {
|
|
||||||
if (err) return cb(err);
|
newTxs = newTxs.concat(lodash.compact(res));
|
||||||
if (exitLoop) {
|
skip = skip + requestLimit;
|
||||||
self.txHistory = [];
|
|
||||||
self.setTxHistory(lodash.compact(txsResult.concat(txsFromLocal)));
|
$log.debug('Syncing TXs. Got:' + newTxs.length + ' Skip:' + skip, ' EndingTxid:', endingTxid, ' Continue:', shouldContinue);
|
||||||
return storageService.setTxHistory(JSON.stringify(self.txHistory), c.walletId, function() {
|
|
||||||
return cb(null);
|
if (!shouldContinue) {
|
||||||
});
|
newTxs = self.processNewTxs(newTxs);
|
||||||
|
$log.debug('Finish Sync: New Txs: ' + newTxs.length);
|
||||||
|
return i_cb(null, newTxs);
|
||||||
}
|
}
|
||||||
fillTxsObject(txsResult, newIndex);
|
|
||||||
|
if (walletId == profileService.focusedClient.credentials.walletId)
|
||||||
|
self.txProgress = newTxs.length;
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
$rootScope.$apply();
|
||||||
|
});
|
||||||
|
getNewTxs(newTxs, skip, i_cb);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getNewTxs([], 0, function(err, txs) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
var newHistory = lodash.compact(txs.concat(txsFromLocal));
|
||||||
|
$log.debug('Tx History synced. Total Txs: ' + newHistory.length);
|
||||||
|
|
||||||
|
if (walletId == profileService.focusedClient.credentials.walletId) {
|
||||||
|
self.completeHistory = newHistory;
|
||||||
|
self.txHistory = newHistory.slice(0, self.historyShowLimit);
|
||||||
|
self.historyShowShowAll = newHistory.length >= self.historyShowLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return storageService.setTxHistory(JSON.stringify(newHistory), walletId, function() {
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
self.showAllHistory = function() {
|
||||||
|
self.historyShowShowAll = false;
|
||||||
|
self.historyRendering = true;
|
||||||
|
$timeout(function() {
|
||||||
|
$rootScope.$apply();
|
||||||
|
$timeout(function() {
|
||||||
|
self.historyRendering = false;
|
||||||
|
self.txHistory = self.completeHistory;
|
||||||
|
}, 100);
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
self.makeTxHistoryRequest = function(txsResult, index, endingTx, cb) {
|
self.getTxsFromServer = function(client, skip, endingTxid, limit, cb) {
|
||||||
var fc = profileService.focusedClient;
|
var res = [];
|
||||||
var c = fc.credentials;
|
|
||||||
var exitLoop = false;
|
|
||||||
|
|
||||||
fc.getTxHistory({
|
client.getTxHistory({
|
||||||
skip: index,
|
skip: skip,
|
||||||
limit: self.limitHistory + 1
|
limit: limit
|
||||||
}, function(err, txsFromBWC) {
|
}, function(err, txsFromServer) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
if (!txsFromBWC[0])
|
if (!txsFromServer.length)
|
||||||
exitLoop = true;
|
return cb();
|
||||||
|
|
||||||
lodash.each(txsFromBWC, function(t) {
|
var res = lodash.takeWhile(txsFromServer, function(tx) {
|
||||||
if (!endingTx) txsResult.push(t);
|
return tx.txid != endingTxid;
|
||||||
else {
|
|
||||||
if (!self.stopSync(t, endingTx) && !exitLoop) {
|
|
||||||
txsResult.push(t);
|
|
||||||
} else {
|
|
||||||
exitLoop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
index = index + self.limitHistory;
|
|
||||||
return cb(null, index, exitLoop);
|
return cb(null, res, res.length == limit);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
self.updateHistory = function() {
|
self.updateHistory = function() {
|
||||||
|
var fc = profileService.focusedClient;
|
||||||
|
var walletId = fc.credentials.walletId;
|
||||||
|
|
||||||
|
if (!fc.isComplete() || self.updatingTxHistory[walletId]) return;
|
||||||
|
|
||||||
$log.debug('Updating Transaction History');
|
$log.debug('Updating Transaction History');
|
||||||
self.txHistoryError = false;
|
self.txHistoryError = false;
|
||||||
self.updatingTxHistory = true;
|
self.updatingTxHistory[walletId] = true;
|
||||||
|
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
self.updateLocalTxHistory(function(err) {
|
self.updateLocalTxHistory(fc, function(err) {
|
||||||
if (err) self.txHistoryError = true;
|
self.updatingTxHistory[walletId] = false;
|
||||||
self.updatingTxHistory = false;
|
if (err)
|
||||||
self.showWaitingSign = false;
|
self.txHistoryError = true;
|
||||||
|
|
||||||
$rootScope.$apply();
|
$rootScope.$apply();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1288,4 +1333,12 @@ angular.module('copayApp.controllers').controller('indexController', function($r
|
||||||
self.setFocusedWallet();
|
self.setFocusedWallet();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$rootScope.$on('Local/NewEncryptionSetting', function() {
|
||||||
|
var fc = profileService.focusedClient;
|
||||||
|
self.isPrivKeyEncrypted = fc.isPrivKeyEncrypted();
|
||||||
|
$timeout(function() {
|
||||||
|
$rootScope.$apply();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('joinController',
|
angular.module('copayApp.controllers').controller('joinController',
|
||||||
function($scope, $rootScope, $timeout, go, notification, profileService, configService, isCordova, storageService, applicationService, $modal, gettext, lodash, ledger, trezor) {
|
function($scope, $rootScope, $timeout, go, notification, profileService, configService, isCordova, storageService, applicationService, $modal, gettext, lodash, ledger, trezor, isChromeApp, isDevel,derivationPathHelper) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var defaults = configService.getDefaults();
|
var defaults = configService.getDefaults();
|
||||||
$scope.bwsurl = defaults.bws.url;
|
$scope.bwsurl = defaults.bws.url;
|
||||||
|
$scope.derivationPath = derivationPathHelper.default;
|
||||||
|
|
||||||
this.onQrCodeScanned = function(data) {
|
this.onQrCodeScanned = function(data) {
|
||||||
$scope.secret = data;
|
$scope.secret = data;
|
||||||
|
@ -13,6 +14,42 @@ angular.module('copayApp.controllers').controller('joinController',
|
||||||
$scope.joinForm.secret.$render();
|
$scope.joinForm.secret.$render();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var updateSeedSourceSelect = function() {
|
||||||
|
self.seedOptions = [{
|
||||||
|
id: 'new',
|
||||||
|
label: gettext('New Random Seed'),
|
||||||
|
}, {
|
||||||
|
id: 'set',
|
||||||
|
label: gettext('Specify Seed...'),
|
||||||
|
}];
|
||||||
|
$scope.seedSource = self.seedOptions[0];
|
||||||
|
|
||||||
|
|
||||||
|
if (isChromeApp) {
|
||||||
|
self.seedOptions.push({
|
||||||
|
id: 'ledger',
|
||||||
|
label: gettext('Ledger Hardware Wallet'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isChromeApp || isDevel) {
|
||||||
|
self.seedOptions.push({
|
||||||
|
id: 'trezor',
|
||||||
|
label: gettext('Trezor Hardware Wallet'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setSeedSource = function(src) {
|
||||||
|
self.seedSourceId = $scope.seedSource.id;
|
||||||
|
self.accountValues = lodash.range(1, 100);
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
$rootScope.$apply();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.join = function(form) {
|
this.join = function(form) {
|
||||||
if (form && form.$invalid) {
|
if (form && form.$invalid) {
|
||||||
self.error = gettext('Please enter the required fields');
|
self.error = gettext('Please enter the required fields');
|
||||||
|
@ -23,10 +60,10 @@ angular.module('copayApp.controllers').controller('joinController',
|
||||||
var opts = {
|
var opts = {
|
||||||
secret: form.secret.$modelValue,
|
secret: form.secret.$modelValue,
|
||||||
myName: form.myName.$modelValue,
|
myName: form.myName.$modelValue,
|
||||||
bwsurl: $scope.bwsurl
|
bwsurl: $scope.bwsurl,
|
||||||
}
|
}
|
||||||
|
|
||||||
var setSeed = form.setSeed.$modelValue;
|
var setSeed = self.seedSourceId =='set';
|
||||||
if (setSeed) {
|
if (setSeed) {
|
||||||
var words = form.privateKey.$modelValue;
|
var words = form.privateKey.$modelValue;
|
||||||
if (words.indexOf(' ') == -1 && words.indexOf('prv') == 1 && words.length > 108) {
|
if (words.indexOf(' ') == -1 && words.indexOf('prv') == 1 && words.length > 108) {
|
||||||
|
@ -35,6 +72,15 @@ angular.module('copayApp.controllers').controller('joinController',
|
||||||
opts.mnemonic = words;
|
opts.mnemonic = words;
|
||||||
}
|
}
|
||||||
opts.passphrase = form.passphrase.$modelValue;
|
opts.passphrase = form.passphrase.$modelValue;
|
||||||
|
|
||||||
|
var pathData = derivationPathHelper.parse($scope.derivationPath);
|
||||||
|
if (!pathData) {
|
||||||
|
this.error = gettext('Invalid derivation path');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
opts.account = pathData.account;
|
||||||
|
opts.networkName = pathData.networkName;
|
||||||
|
opts.derivationStrategy = pathData.derivationStrategy;
|
||||||
} else {
|
} else {
|
||||||
opts.passphrase = form.createPassphrase.$modelValue;
|
opts.passphrase = form.createPassphrase.$modelValue;
|
||||||
}
|
}
|
||||||
|
@ -44,12 +90,21 @@ angular.module('copayApp.controllers').controller('joinController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (form.hwLedger.$modelValue || form.hwTrezor.$modelValue) {
|
if (self.seedSourceId == 'ledger' || self.seedSourceId == 'trezor') {
|
||||||
self.hwWallet = form.hwLedger.$modelValue ? 'Ledger' : 'TREZOR';
|
var account = $scope.account;
|
||||||
var src = form.hwLedger.$modelValue ? ledger : trezor;
|
if (!account || account < 1) {
|
||||||
|
this.error = gettext('Invalid account number');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var account = 0;
|
if ( self.seedSourceId == 'trezor')
|
||||||
src.getInfoForNewWallet(account, function(err, lopts) {
|
account = account - 1;
|
||||||
|
|
||||||
|
opts.account = account;
|
||||||
|
self.hwWallet = self.seedSourceId == 'ledger' ? 'Ledger' : 'Trezor';
|
||||||
|
var src = self.seedSourceId == 'ledger' ? ledger : trezor;
|
||||||
|
|
||||||
|
src.getInfoForNewWallet(true, account, function(err, lopts) {
|
||||||
self.hwWallet = false;
|
self.hwWallet = false;
|
||||||
if (err) {
|
if (err) {
|
||||||
self.error = err;
|
self.error = err;
|
||||||
|
@ -82,4 +137,7 @@ angular.module('copayApp.controllers').controller('joinController',
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updateSeedSourceSelect();
|
||||||
|
self.setSeedSource('new');
|
||||||
});
|
});
|
||||||
|
|
|
@ -55,6 +55,7 @@ angular.module('copayApp.controllers').controller('preferencesController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
profileService.setPrivateKeyEncryptionFC(password, function() {
|
profileService.setPrivateKeyEncryptionFC(password, function() {
|
||||||
|
$rootScope.$emit('Local/NewEncryptionSetting');
|
||||||
$scope.encrypt = true;
|
$scope.encrypt = true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -66,6 +67,7 @@ angular.module('copayApp.controllers').controller('preferencesController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
profileService.disablePrivateKeyEncryptionFC(function(err) {
|
profileService.disablePrivateKeyEncryptionFC(function(err) {
|
||||||
|
$rootScope.$emit('Local/NewEncryptionSetting');
|
||||||
if (err) {
|
if (err) {
|
||||||
$scope.encrypt = true;
|
$scope.encrypt = true;
|
||||||
$log.error(err);
|
$log.error(err);
|
||||||
|
|
|
@ -13,7 +13,7 @@ angular.module('copayApp.controllers').controller('preferencesBwsUrlController',
|
||||||
this.bwsurl = (config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url;
|
this.bwsurl = (config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url;
|
||||||
|
|
||||||
this.resetDefaultUrl = function() {
|
this.resetDefaultUrl = function() {
|
||||||
this.bwsurl = 'https://bws.bitpay.com/bws/api';
|
this.bwsurl = defaults.bws.url;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.save = function() {
|
this.save = function() {
|
||||||
|
@ -50,4 +50,4 @@ angular.module('copayApp.controllers').controller('preferencesBwsUrlController',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ angular.module('copayApp.controllers').controller('preferencesInformation',
|
||||||
var c = fc.credentials;
|
var c = fc.credentials;
|
||||||
|
|
||||||
this.init = function() {
|
this.init = function() {
|
||||||
var basePath = profileService.getUtils().getBaseAddressDerivationPath(c.derivationStrategy, c.network, 0);
|
var basePath = c.getBaseAddressDerivationPath();
|
||||||
|
|
||||||
$scope.walletName = c.walletName;
|
$scope.walletName = c.walletName;
|
||||||
$scope.walletId = c.walletId;
|
$scope.walletId = c.walletId;
|
||||||
|
|
|
@ -106,36 +106,6 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
|
||||||
var cancel_msg = gettextCatalog.getString('Cancel');
|
var cancel_msg = gettextCatalog.getString('Cancel');
|
||||||
var confirm_msg = gettextCatalog.getString('Confirm');
|
var confirm_msg = gettextCatalog.getString('Confirm');
|
||||||
|
|
||||||
$scope.openCopayersModal = function(copayers, copayerId) {
|
|
||||||
$rootScope.modalOpened = true;
|
|
||||||
var fc = profileService.focusedClient;
|
|
||||||
|
|
||||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
|
||||||
$scope.copayers = copayers;
|
|
||||||
$scope.copayerId = copayerId;
|
|
||||||
$scope.color = fc.backgroundColor;
|
|
||||||
$scope.cancel = function() {
|
|
||||||
$modalInstance.dismiss('cancel');
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var modalInstance = $modal.open({
|
|
||||||
templateUrl: 'views/modals/copayers.html',
|
|
||||||
windowClass: animationService.modalAnimated.slideUp,
|
|
||||||
controller: ModalInstanceCtrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
var disableCloseModal = $rootScope.$on('closeModal', function() {
|
|
||||||
modalInstance.dismiss('cancel');
|
|
||||||
});
|
|
||||||
|
|
||||||
modalInstance.result.finally(function() {
|
|
||||||
$rootScope.modalOpened = false;
|
|
||||||
disableCloseModal();
|
|
||||||
var m = angular.element(document.getElementsByClassName('reveal-modal'));
|
|
||||||
m.addClass(animationService.modalAnimated.slideOutDown);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.openDestinationAddressModal = function(wallets, address) {
|
$scope.openDestinationAddressModal = function(wallets, address) {
|
||||||
$rootScope.modalOpened = true;
|
$rootScope.modalOpened = true;
|
||||||
var fc = profileService.focusedClient;
|
var fc = profileService.focusedClient;
|
||||||
|
|
|
@ -21,13 +21,14 @@ angular
|
||||||
|
|
||||||
$logProvider.debugEnabled(true);
|
$logProvider.debugEnabled(true);
|
||||||
$provide.decorator('$log', ['$delegate',
|
$provide.decorator('$log', ['$delegate',
|
||||||
function($delegate) {
|
function($delegate, isDevel) {
|
||||||
var historicLog = historicLogProvider.$get();
|
var historicLog = historicLogProvider.$get();
|
||||||
|
|
||||||
['debug', 'info', 'warn', 'error', 'log'].forEach(function(level) {
|
['debug', 'info', 'warn', 'error', 'log'].forEach(function(level) {
|
||||||
|
if (isDevel && level == 'error') return;
|
||||||
|
|
||||||
var orig = $delegate[level];
|
var orig = $delegate[level];
|
||||||
$delegate[level] = function() {
|
$delegate[level] = function() {
|
||||||
|
|
||||||
if (level == 'error')
|
if (level == 'error')
|
||||||
console.log(arguments);
|
console.log(arguments);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('copayApp.services').factory('derivationPathHelper', function(lodash) {
|
||||||
|
var root = {};
|
||||||
|
|
||||||
|
root.default = "m/44'/0'/0'"
|
||||||
|
root.parse = function(str) {
|
||||||
|
var arr = str.split('/');
|
||||||
|
|
||||||
|
var ret = {};
|
||||||
|
|
||||||
|
if (arr[0] != 'm')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (arr[1]) {
|
||||||
|
case "44'":
|
||||||
|
ret.derivationStrategy = 'BIP44';
|
||||||
|
break;
|
||||||
|
case "48'":
|
||||||
|
ret.derivationStrategy = 'BIP48';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (arr[2]) {
|
||||||
|
case "0'":
|
||||||
|
ret.networkName = 'livenet';
|
||||||
|
break;
|
||||||
|
case "1'":
|
||||||
|
ret.networkName = 'testnet';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var match = arr[3].match(/(\d+)'/);
|
||||||
|
if (!match)
|
||||||
|
return false;
|
||||||
|
ret.account = + match[1]
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
return root;
|
||||||
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('copayApp.services')
|
||||||
|
.factory('hwWallet', function($log, bwcService) {
|
||||||
|
var root = {};
|
||||||
|
|
||||||
|
// Ledger magic number to get xPub without user confirmation
|
||||||
|
root.ENTROPY_INDEX_PATH = "0xb11e/";
|
||||||
|
root.UNISIG_ROOTPATH = 44;
|
||||||
|
root.MULTISIG_ROOTPATH = 48;
|
||||||
|
root.LIVENET_PATH = 0;
|
||||||
|
|
||||||
|
root._err = function(data) {
|
||||||
|
var msg = 'Hardware Wallet Error: ' + (data.error || data.message || 'unknown');
|
||||||
|
$log.warn(msg);
|
||||||
|
return msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
root.getRootPath = function(device, isMultisig, account) {
|
||||||
|
if (!isMultisig) return root.UNISIG_ROOTPATH;
|
||||||
|
|
||||||
|
// Compat
|
||||||
|
if (device == 'ledger' && account ==0) return root.UNISIG_ROOTPATH;
|
||||||
|
|
||||||
|
return root.MULTISIG_ROOTPATH;
|
||||||
|
};
|
||||||
|
|
||||||
|
root.getAddressPath = function(device, isMultisig, account) {
|
||||||
|
return root.getRootPath(device,isMultisig,account) + "'/" + root.LIVENET_PATH + "'/" + account + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
root.getEntropyPath = function(device, isMultisig, account) {
|
||||||
|
var path;
|
||||||
|
|
||||||
|
// Old ledger wallet compat
|
||||||
|
if (device == 'ledger' && account == 0)
|
||||||
|
return root.ENTROPY_INDEX_PATH + "0'";
|
||||||
|
|
||||||
|
return root.ENTROPY_INDEX_PATH + root.getRootPath(device,isMultisig,account) + "'/" + account + "'";
|
||||||
|
};
|
||||||
|
|
||||||
|
root.pubKeyToEntropySource = function(xPubKey) {
|
||||||
|
var b = bwcService.getBitcore();
|
||||||
|
var x = b.HDPublicKey(xPubKey);
|
||||||
|
return x.publicKey.toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
return root;
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('copayApp.services').factory('isDevel', function(nodeWebkit, isChromeApp, isMobile) {
|
||||||
|
return !isMobile.any() && !isChromeApp && !nodeWebkit.isDefined();
|
||||||
|
});
|
|
@ -1,43 +1,27 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.services')
|
angular.module('copayApp.services')
|
||||||
.factory('ledger', function($log, bwcService, gettext) {
|
.factory('ledger', function($log, bwcService, gettext, hwWallet) {
|
||||||
var root = {};
|
var root = {};
|
||||||
var LEDGER_CHROME_ID = "kkdpmhnladdopljabkgpacgpliggeeaf";
|
var LEDGER_CHROME_ID = "kkdpmhnladdopljabkgpacgpliggeeaf";
|
||||||
|
|
||||||
// Ledger magic number to get xPub without user confirmation
|
|
||||||
root.ENTROPY_INDEX_PATH = "0xb11e/";
|
|
||||||
|
|
||||||
root.callbacks = {};
|
root.callbacks = {};
|
||||||
|
|
||||||
root.hasSession = function() {
|
root.hasSession = function() {
|
||||||
root._message({
|
root._message({
|
||||||
command: "has_session"
|
command: "has_session"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
root.getEntropySource = function(account, callback) {
|
root.getEntropySource = function(isMultisig, account, callback) {
|
||||||
var path = root.ENTROPY_INDEX_PATH + account + "'";
|
root.getXPubKey(hwWallet.getEntropyPath('ledger', isMultisig, account), function(data) {
|
||||||
var xpub = root.getXPubKey(path, function(data) {
|
if (!data.success)
|
||||||
if (!data.success) {
|
return callback(hwWallet._err(data));
|
||||||
$log.warn(data.message);
|
|
||||||
return callback(data);
|
return callback(null, hwWallet.pubKeyToEntropySource(data.xpubkey));
|
||||||
}
|
|
||||||
|
|
||||||
var b = bwcService.getBitcore();
|
|
||||||
|
|
||||||
var x = b.HDPublicKey(data.xpubkey);
|
|
||||||
data.entropySource = x.publicKey.toString();
|
|
||||||
return callback(data);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root.getXPubKeyForAddresses = function(account, callback) {
|
|
||||||
return root.getXPubKey(root._getPath(account), callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
root.getXPubKey = function(path, callback) {
|
root.getXPubKey = function(path, callback) {
|
||||||
|
|
||||||
$log.debug('Ledger deriving xPub path:', path);
|
$log.debug('Ledger deriving xPub path:', path);
|
||||||
root.callbacks["get_xpubkey"] = callback;
|
root.callbacks["get_xpubkey"] = callback;
|
||||||
root._messageAfterSession({
|
root._messageAfterSession({
|
||||||
|
@ -47,35 +31,36 @@ angular.module('copayApp.services')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
root.getInfoForNewWallet = function(account, callback) {
|
root.getInfoForNewWallet = function(isMultisig, account, callback) {
|
||||||
var opts = {};
|
var opts = {};
|
||||||
root.getEntropySource(account, function(data) {
|
root.getEntropySource(isMultisig, account, function(err, entropySource) {
|
||||||
if (!data.success) {
|
if (err) return callback(err);
|
||||||
$log.warn(data.message);
|
|
||||||
return callback(data.message);
|
opts.entropySource = entropySource;
|
||||||
}
|
root.getXPubKey(hwWallet.getAddressPath('ledger', isMultisig, account), function(data) {
|
||||||
opts.entropySource = data.entropySource;
|
|
||||||
root.getXPubKeyForAddresses(account, function(data) {
|
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
$log.warn(data.message);
|
$log.warn(data.message);
|
||||||
return callback(data);
|
return callback(data);
|
||||||
}
|
}
|
||||||
opts.extendedPublicKey = data.xpubkey;
|
opts.extendedPublicKey = data.xpubkey;
|
||||||
opts.externalSource = 'ledger';
|
opts.externalSource = 'ledger';
|
||||||
opts.externalIndex = account;
|
opts.account = account;
|
||||||
|
|
||||||
|
// Old ledger compat
|
||||||
|
opts.derivationStrategy = account ? 'BIP48' : 'BIP44';
|
||||||
return callback(null, opts);
|
return callback(null, opts);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root._signP2SH = function(txp, account, callback) {
|
root._signP2SH = function(txp, account, isMultisig, callback) {
|
||||||
root.callbacks["sign_p2sh"] = callback;
|
root.callbacks["sign_p2sh"] = callback;
|
||||||
var redeemScripts = [];
|
var redeemScripts = [];
|
||||||
var paths = [];
|
var paths = [];
|
||||||
var tx = bwcService.getUtils().buildTx(txp);
|
var tx = bwcService.buildTx(txp);
|
||||||
for (var i = 0; i < tx.inputs.length; i++) {
|
for (var i = 0; i < tx.inputs.length; i++) {
|
||||||
redeemScripts.push(new ByteString(tx.inputs[i].redeemScript.toBuffer().toString('hex'), GP.HEX).toString());
|
redeemScripts.push(new ByteString(tx.inputs[i].redeemScript.toBuffer().toString('hex'), GP.HEX).toString());
|
||||||
paths.push(root._getPath(account) + txp.inputs[i].path.substring(1));
|
paths.push(hwWallet.getAddressPath('ledger', isMultisig, account) + txp.inputs[i].path.substring(1));
|
||||||
}
|
}
|
||||||
var splitTransaction = root._splitTransaction(new ByteString(tx.toString(), GP.HEX));
|
var splitTransaction = root._splitTransaction(new ByteString(tx.toString(), GP.HEX));
|
||||||
var inputs = [];
|
var inputs = [];
|
||||||
|
@ -98,12 +83,15 @@ angular.module('copayApp.services')
|
||||||
};
|
};
|
||||||
|
|
||||||
root.signTx = function(txp, account, callback) {
|
root.signTx = function(txp, account, callback) {
|
||||||
|
|
||||||
|
// TODO Compat
|
||||||
|
var isMultisig = true;
|
||||||
if (txp.addressType == 'P2PKH') {
|
if (txp.addressType == 'P2PKH') {
|
||||||
var msg = 'P2PKH wallets are not supported with ledger';
|
var msg = 'P2PKH wallets are not supported with ledger';
|
||||||
$log.error(msg);
|
$log.error(msg);
|
||||||
return callback(msg);
|
return callback(msg);
|
||||||
} else {
|
} else {
|
||||||
root._signP2SH(txp, account, callback);
|
root._signP2SH(txp, account, isMultisig, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,10 +142,6 @@ angular.module('copayApp.services')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
root._getPath = function(account) {
|
|
||||||
return "44'/0'/" + account + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
root._splitTransaction = function(transaction) {
|
root._splitTransaction = function(transaction) {
|
||||||
var result = {};
|
var result = {};
|
||||||
var inputs = [];
|
var inputs = [];
|
||||||
|
|
|
@ -46,8 +46,6 @@ angular.module('copayApp.services')
|
||||||
client.setNotificationsInterval(BACKGROUND_UPDATE_PERIOD);
|
client.setNotificationsInterval(BACKGROUND_UPDATE_PERIOD);
|
||||||
});
|
});
|
||||||
root.focusedClient.setNotificationsInterval(FOREGROUND_UPDATE_PERIOD);
|
root.focusedClient.setNotificationsInterval(FOREGROUND_UPDATE_PERIOD);
|
||||||
|
|
||||||
console.log('[profileService.js.49] SETTING...'); //TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cb();
|
return cb();
|
||||||
|
@ -175,14 +173,17 @@ angular.module('copayApp.services')
|
||||||
var walletClient = bwcService.getClient();
|
var walletClient = bwcService.getClient();
|
||||||
var network = opts.networkName || 'livenet';
|
var network = opts.networkName || 'livenet';
|
||||||
|
|
||||||
|
|
||||||
if (opts.mnemonic) {
|
if (opts.mnemonic) {
|
||||||
try {
|
try {
|
||||||
opts.mnemonic = root._normalizeMnemonic(opts.mnemonic);
|
opts.mnemonic = root._normalizeMnemonic(opts.mnemonic);
|
||||||
walletClient.seedFromMnemonic(opts.mnemonic, {
|
walletClient.seedFromMnemonic(opts.mnemonic, {
|
||||||
network: network,
|
network: network,
|
||||||
passphrase: opts.passphrase,
|
passphrase: opts.passphrase,
|
||||||
account: 0,
|
account: opts.account || 0,
|
||||||
|
derivationStrategy: opts.derivationStrategy || 'BIP44',
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
$log.info(ex);
|
$log.info(ex);
|
||||||
return cb(gettext('Could not create: Invalid wallet seed'));
|
return cb(gettext('Could not create: Invalid wallet seed'));
|
||||||
|
@ -197,7 +198,8 @@ angular.module('copayApp.services')
|
||||||
} else if (opts.extendedPublicKey) {
|
} else if (opts.extendedPublicKey) {
|
||||||
try {
|
try {
|
||||||
walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
|
walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
|
||||||
account: 0
|
account: opts.account || 0,
|
||||||
|
derivationStrategy: opts.derivationStrategy || 'BIP44',
|
||||||
});
|
});
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
$log.warn("Creating wallet from Extended Public Key Arg:", ex, opts);
|
$log.warn("Creating wallet from Extended Public Key Arg:", ex, opts);
|
||||||
|
@ -311,7 +313,7 @@ angular.module('copayApp.services')
|
||||||
walletId: walletId
|
walletId: walletId
|
||||||
});
|
});
|
||||||
|
|
||||||
delete root.walletClients[walletId];
|
delete root.walletClients[walletId];
|
||||||
root.focusedClient = null;
|
root.focusedClient = null;
|
||||||
|
|
||||||
storageService.clearLastAddress(walletId, function(err) {
|
storageService.clearLastAddress(walletId, function(err) {
|
||||||
|
@ -361,7 +363,7 @@ angular.module('copayApp.services')
|
||||||
root.setWalletClients();
|
root.setWalletClients();
|
||||||
|
|
||||||
root.setAndStoreFocus(walletId, function() {
|
root.setAndStoreFocus(walletId, function() {
|
||||||
storageService.storeProfile(root.profile, function(err){
|
storageService.storeProfile(root.profile, function(err) {
|
||||||
return cb(err, walletId);
|
return cb(err, walletId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -420,7 +422,7 @@ angular.module('copayApp.services')
|
||||||
walletClient.importFromMnemonic(words, {
|
walletClient.importFromMnemonic(words, {
|
||||||
network: opts.networkName,
|
network: opts.networkName,
|
||||||
passphrase: opts.passphrase,
|
passphrase: opts.passphrase,
|
||||||
account: 0,
|
account: opts.account || 0,
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return bwsError.cb(err, gettext('Could not import'), cb);
|
return bwsError.cb(err, gettext('Could not import'), cb);
|
||||||
|
@ -437,7 +439,8 @@ angular.module('copayApp.services')
|
||||||
$log.debug('Importing Wallet XPubKey');
|
$log.debug('Importing Wallet XPubKey');
|
||||||
|
|
||||||
walletClient.importFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
|
walletClient.importFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
|
||||||
account: 0
|
account: opts.account || 0,
|
||||||
|
derivationStrategy: opts.derivationStrategy || 'BIP44',
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
||||||
|
@ -594,7 +597,7 @@ angular.module('copayApp.services')
|
||||||
var fc = root.focusedClient;
|
var fc = root.focusedClient;
|
||||||
$log.info('Requesting Ledger Chrome app to sign the transaction');
|
$log.info('Requesting Ledger Chrome app to sign the transaction');
|
||||||
|
|
||||||
ledger.signTx(txp, 0, function(result) {
|
ledger.signTx(txp, fc.credentials.account, function(result) {
|
||||||
$log.debug('Ledger response', result);
|
$log.debug('Ledger response', result);
|
||||||
if (!result.success)
|
if (!result.success)
|
||||||
return cb(result.message || result.error);
|
return cb(result.message || result.error);
|
||||||
|
@ -612,7 +615,7 @@ angular.module('copayApp.services')
|
||||||
$log.info('Requesting Trezor to sign the transaction');
|
$log.info('Requesting Trezor to sign the transaction');
|
||||||
|
|
||||||
var xPubKeys = lodash.pluck(fc.credentials.publicKeyRing, 'xPubKey');
|
var xPubKeys = lodash.pluck(fc.credentials.publicKeyRing, 'xPubKey');
|
||||||
trezor.signTx(xPubKeys, txp, 0, function(err, result) {
|
trezor.signTx(xPubKeys, txp, fc.credentials.account, function(err, result) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
$log.debug('Trezor response', result);
|
$log.debug('Trezor response', result);
|
||||||
|
|
|
@ -1,39 +1,21 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.services')
|
angular.module('copayApp.services')
|
||||||
.factory('trezor', function($log, $timeout, bwcService, gettext, lodash, bitcore) {
|
.factory('trezor', function($log, $timeout, gettext, lodash, bitcore, hwWallet) {
|
||||||
var root = {};
|
var root = {};
|
||||||
|
|
||||||
var SETTLE_TIME = 3000;
|
var SETTLE_TIME = 3000;
|
||||||
|
|
||||||
root.ENTROPY_INDEX_PATH = "0xb11e/";
|
|
||||||
root.callbacks = {};
|
root.callbacks = {};
|
||||||
|
|
||||||
|
root.getEntropySource = function(isMultisig, account, callback) {
|
||||||
root._err = function(data) {
|
root.getXPubKey(hwWallet.getEntropyPath('trezor', isMultisig, account), function(data) {
|
||||||
var msg = 'TREZOR Error: ' + (data.error || data.message || 'unknown');
|
if (!data.success)
|
||||||
$log.warn(msg);
|
return callback(hwWallet._err(data));
|
||||||
return msg;
|
|
||||||
};
|
return callback(null, hwWallet.pubKeyToEntropySource(data.xpubkey));
|
||||||
|
|
||||||
root.getEntropySource = function(account, callback) {
|
|
||||||
var path = root.ENTROPY_INDEX_PATH + account + "'";
|
|
||||||
var xpub = root.getXPubKey(path, function(data) {
|
|
||||||
if (!data.success) {
|
|
||||||
return callback(root._err(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
var b = bwcService.getBitcore();
|
|
||||||
|
|
||||||
var x = b.HDPublicKey(data.xpubkey);
|
|
||||||
data.entropySource = x.publicKey.toString();
|
|
||||||
return callback(null, data);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root.getXPubKeyForAddresses = function(account, callback) {
|
|
||||||
return root.getXPubKey(root._getPath(account), callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
root.getXPubKey = function(path, callback) {
|
root.getXPubKey = function(path, callback) {
|
||||||
$log.debug('TREZOR deriving xPub path:', path);
|
$log.debug('TREZOR deriving xPub path:', path);
|
||||||
|
@ -41,20 +23,25 @@ angular.module('copayApp.services')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
root.getInfoForNewWallet = function(account, callback) {
|
root.getInfoForNewWallet = function(isMultisig, account, callback) {
|
||||||
var opts = {};
|
var opts = {};
|
||||||
root.getEntropySource(account, function(err, data) {
|
root.getEntropySource(isMultisig, account, function(err, data) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
opts.entropySource = data.entropySource;
|
opts.entropySource = data;
|
||||||
$log.debug('Waiting TREZOR to settle...');
|
$log.debug('Waiting TREZOR to settle...');
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
root.getXPubKeyForAddresses(account, function(data) {
|
|
||||||
if (!data.success)
|
root.getXPubKey(hwWallet.getAddressPath('trezor', isMultisig, account), function(data) {
|
||||||
return callback(root._err(data));
|
if (!data.success)
|
||||||
|
return callback(hwWallet._err(data));
|
||||||
|
|
||||||
opts.extendedPublicKey = data.xpubkey;
|
opts.extendedPublicKey = data.xpubkey;
|
||||||
opts.externalSource = 'trezor';
|
opts.externalSource = 'trezor';
|
||||||
opts.externalIndex = account;
|
opts.account = account;
|
||||||
|
|
||||||
|
if (isMultisig)
|
||||||
|
opts.derivationStrategy = 'BIP48';
|
||||||
|
|
||||||
return callback(null, opts);
|
return callback(null, opts);
|
||||||
});
|
});
|
||||||
}, SETTLE_TIME);
|
}, SETTLE_TIME);
|
||||||
|
@ -107,10 +94,13 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
if (txp.addressType == 'P2PKH') {
|
if (txp.addressType == 'P2PKH') {
|
||||||
|
|
||||||
|
$log.debug("Trezor signing uni-sig p2pkh. Account:", account);
|
||||||
|
|
||||||
var inAmount = 0;
|
var inAmount = 0;
|
||||||
inputs = lodash.map(txp.inputs, function(i) {
|
inputs = lodash.map(txp.inputs, function(i) {
|
||||||
|
$log.debug("Trezor TX input path:", i.path);
|
||||||
var pathArr = i.path.split('/');
|
var pathArr = i.path.split('/');
|
||||||
var n = [44 | 0x80000000, 0 | 0x80000000, account | 0x80000000, parseInt(pathArr[1]), parseInt(pathArr[2])];
|
var n = [hwWallet.UNISIG_ROOTPATH | 0x80000000, 0 | 0x80000000, account | 0x80000000, parseInt(pathArr[1]), parseInt(pathArr[2])];
|
||||||
inAmount += i.satoshis;
|
inAmount += i.satoshis;
|
||||||
return {
|
return {
|
||||||
address_n: n,
|
address_n: n,
|
||||||
|
@ -121,8 +111,9 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
var change = inAmount - txp.fee - txp.amount;
|
var change = inAmount - txp.fee - txp.amount;
|
||||||
if (change > 0) {
|
if (change > 0) {
|
||||||
|
$log.debug("Trezor TX change path:", txp.changeAddress.path);
|
||||||
var pathArr = txp.changeAddress.path.split('/');
|
var pathArr = txp.changeAddress.path.split('/');
|
||||||
var n = [44 | 0x80000000, 0 | 0x80000000, account | 0x80000000, parseInt(pathArr[1]), parseInt(pathArr[2])];
|
var n = [hwWallet.UNISIG_ROOTPATH | 0x80000000, 0 | 0x80000000, account | 0x80000000, parseInt(pathArr[1]), parseInt(pathArr[2])];
|
||||||
|
|
||||||
tmpOutputs.push({
|
tmpOutputs.push({
|
||||||
address_n: n,
|
address_n: n,
|
||||||
|
@ -133,8 +124,9 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// P2SH Wallet
|
// P2SH Wallet, multisig wallet
|
||||||
var inAmount = 0;
|
var inAmount = 0;
|
||||||
|
$log.debug("Trezor signing multi-sig p2sh. Account:", account);
|
||||||
|
|
||||||
var sigs = xPubKeys.map(function(v) {
|
var sigs = xPubKeys.map(function(v) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -142,8 +134,9 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
|
|
||||||
inputs = lodash.map(txp.inputs, function(i) {
|
inputs = lodash.map(txp.inputs, function(i) {
|
||||||
|
$log.debug("Trezor TX input path:", i.path);
|
||||||
var pathArr = i.path.split('/');
|
var pathArr = i.path.split('/');
|
||||||
var n = [44 | 0x80000000, 0 | 0x80000000, account | 0x80000000, parseInt(pathArr[1]), parseInt(pathArr[2])];
|
var n = [hwWallet.MULTISIG_ROOTPATH | 0x80000000, 0 | 0x80000000, account | 0x80000000, parseInt(pathArr[1]), parseInt(pathArr[2])];
|
||||||
var np = n.slice(3);
|
var np = n.slice(3);
|
||||||
|
|
||||||
inAmount += i.satoshis;
|
inAmount += i.satoshis;
|
||||||
|
@ -171,8 +164,9 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
var change = inAmount - txp.fee - txp.amount;
|
var change = inAmount - txp.fee - txp.amount;
|
||||||
if (change > 0) {
|
if (change > 0) {
|
||||||
|
$log.debug("Trezor TX change path:", txp.changeAddress.path);
|
||||||
var pathArr = txp.changeAddress.path.split('/');
|
var pathArr = txp.changeAddress.path.split('/');
|
||||||
var n = [44 | 0x80000000, 0 | 0x80000000, account | 0x80000000, parseInt(pathArr[1]), parseInt(pathArr[2])];
|
var n = [hwWallet.MULTISIG_ROOTPATH | 0x80000000, 0 | 0x80000000, account | 0x80000000, parseInt(pathArr[1]), parseInt(pathArr[2])];
|
||||||
var np = n.slice(3);
|
var np = n.slice(3);
|
||||||
|
|
||||||
var orderedPubKeys = root._orderPubKeys(xPubKeys, np);
|
var orderedPubKeys = root._orderPubKeys(xPubKeys, np);
|
||||||
|
@ -183,18 +177,6 @@ angular.module('copayApp.services')
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 6D
|
|
||||||
// 6C
|
|
||||||
// Addr: 3HFkHufeSaqJtqby8G9RiajaL6HdQDypRT
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//(sin reverse)
|
|
||||||
// 6C
|
|
||||||
// 6D
|
|
||||||
// Addr: 3KCPRDXpmovs9nFvJHJjjsyoBDXXUZ2Frg
|
|
||||||
// "asm" : "2 03e53b2f69e1705b253029aae2591fbd0e799ed8071c8588a545b2d472dd12df88 0379797abc21d6f82c7f0aba78fd3888d8ae75ec56a10509b20feedbeac20285d9 2 OP_CHECKMULTISIG",
|
|
||||||
//
|
|
||||||
|
|
||||||
tmpOutputs.push({
|
tmpOutputs.push({
|
||||||
address_n: n,
|
address_n: n,
|
||||||
amount: change,
|
amount: change,
|
||||||
|
@ -226,17 +208,13 @@ angular.module('copayApp.services')
|
||||||
outputs = JSON.parse(JSON.stringify(outputs));
|
outputs = JSON.parse(JSON.stringify(outputs));
|
||||||
|
|
||||||
$log.debug('Signing with TREZOR', inputs, outputs);
|
$log.debug('Signing with TREZOR', inputs, outputs);
|
||||||
TrezorConnect.signTx(inputs, outputs, function(result) {
|
TrezorConnect.signTx(inputs, outputs, function(res) {
|
||||||
if (!data.success)
|
if (!res.success)
|
||||||
return callback(root._err(data));
|
return callback(hwWallet._err(res));
|
||||||
|
|
||||||
callback(null, result);
|
callback(null, res);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root._getPath = function(account) {
|
|
||||||
return "44'/0'/" + account + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
window.TREZOR_CHROME_URL = './bower_components/trezor-connect/chrome/wrapper.html';
|
||||||
|
|
372
src/js/trezor.js
372
src/js/trezor.js
|
@ -1,372 +0,0 @@
|
||||||
window.TrezorConnect = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var CONNECT_ORIGIN = 'https://trezor.github.io';
|
|
||||||
var CONNECT_PATH = CONNECT_ORIGIN + '/connect';
|
|
||||||
var CONNECT_POPUP = CONNECT_PATH + '/popup/popup.html';
|
|
||||||
|
|
||||||
var ERR_TIMED_OUT = 'Loading timed out';
|
|
||||||
var ERR_WINDOW_CLOSED = 'Window closed';
|
|
||||||
var ERR_ALREADY_WAITING = 'Already waiting for a response';
|
|
||||||
|
|
||||||
var manager = new PopupManager(
|
|
||||||
CONNECT_POPUP,
|
|
||||||
CONNECT_ORIGIN,
|
|
||||||
'trezor-connect',
|
|
||||||
function () {
|
|
||||||
var w = 600;
|
|
||||||
var h = 500;
|
|
||||||
var x = (screen.width - w) / 2;
|
|
||||||
var y = (screen.height - h) / 3;
|
|
||||||
var params =
|
|
||||||
'height=' + h +
|
|
||||||
',width=' + w +
|
|
||||||
',left=' + x +
|
|
||||||
',top=' + y +
|
|
||||||
',menubar=no' +
|
|
||||||
',toolbar=no' +
|
|
||||||
',location=no' +
|
|
||||||
',personalbar=no' +
|
|
||||||
',status=no';
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public API.
|
|
||||||
*/
|
|
||||||
function TrezorConnect() {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Popup errors.
|
|
||||||
*/
|
|
||||||
this.ERR_TIMED_OUT = ERR_TIMED_OUT;
|
|
||||||
this.ERR_WINDOW_CLOSED = ERR_WINDOW_CLOSED;
|
|
||||||
this.ERR_ALREADY_WAITING = ERR_ALREADY_WAITING;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef XPubKeyResult
|
|
||||||
* @param {boolean} success
|
|
||||||
* @param {?string} error
|
|
||||||
* @param {?string} xpubkey serialized extended public key
|
|
||||||
* @param {?string} path BIP32 serializd path of the key
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load BIP32 extended public key by path.
|
|
||||||
*
|
|
||||||
* Path can be specified either in the string form ("m/44'/1/0") or as
|
|
||||||
* raw integer array. In case you omit the path, user is asked to select
|
|
||||||
* a BIP32 account to export, and the result contains m/44'/0'/x' node
|
|
||||||
* of the account.
|
|
||||||
*
|
|
||||||
* @param {?(string|array<number>)} path
|
|
||||||
* @param {function(XPubKeyResult)} callback
|
|
||||||
*/
|
|
||||||
this.getXPubKey = function (path, callback) {
|
|
||||||
if (typeof path === 'string') {
|
|
||||||
path = parseHDPath(path);
|
|
||||||
}
|
|
||||||
manager.sendWithChannel({
|
|
||||||
'type': 'xpubkey',
|
|
||||||
'path': path
|
|
||||||
}, function (result) {
|
|
||||||
manager.close();
|
|
||||||
callback(result);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef SignTxResult
|
|
||||||
* @param {boolean} success
|
|
||||||
* @param {?string} error
|
|
||||||
* @param {?string} serialized_tx serialized tx, in hex, including signatures
|
|
||||||
* @param {?array<string>} signatures array of input signatures, in hex
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a transaction in the device and return both serialized
|
|
||||||
* transaction and the signatures.
|
|
||||||
*
|
|
||||||
* @param {array<TxInputType>} inputs
|
|
||||||
* @param {array<TxOutputType>} outputs
|
|
||||||
* @param {function(SignTxResult)} callback
|
|
||||||
*
|
|
||||||
* @see https://github.com/trezor/trezor-common/blob/master/protob/types.proto
|
|
||||||
*/
|
|
||||||
this.signTx = function (inputs, outputs, callback) {
|
|
||||||
manager.sendWithChannel({
|
|
||||||
'type': 'signtx',
|
|
||||||
'inputs': inputs,
|
|
||||||
'outputs': outputs
|
|
||||||
}, function (result) {
|
|
||||||
manager.close();
|
|
||||||
callback(result);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef RequestLoginResult
|
|
||||||
* @param {boolean} success
|
|
||||||
* @param {?string} error
|
|
||||||
* @param {?string} public_key public key used for signing, in hex
|
|
||||||
* @param {?string} signature signature, in hex
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a login challenge for active origin.
|
|
||||||
*
|
|
||||||
* @param {?string} hosticon
|
|
||||||
* @param {string} challenge_hidden
|
|
||||||
* @param {string} challenge_visual
|
|
||||||
* @param {string|function(RequestLoginResult)} callback
|
|
||||||
*
|
|
||||||
* @see https://github.com/trezor/trezor-common/blob/master/protob/messages.proto
|
|
||||||
*/
|
|
||||||
this.requestLogin = function (
|
|
||||||
hosticon,
|
|
||||||
challenge_hidden,
|
|
||||||
challenge_visual,
|
|
||||||
callback
|
|
||||||
) {
|
|
||||||
if (typeof callback === 'string') {
|
|
||||||
// special case for a login through <trezor:login> button.
|
|
||||||
// `callback` is name of global var
|
|
||||||
callback = window[callback];
|
|
||||||
}
|
|
||||||
if (!callback) {
|
|
||||||
throw new TypeError('TrezorConnect: login callback not found');
|
|
||||||
}
|
|
||||||
manager.sendWithChannel({
|
|
||||||
'type': 'login',
|
|
||||||
'icon': hosticon,
|
|
||||||
'challenge_hidden': challenge_hidden,
|
|
||||||
'challenge_visual': challenge_visual
|
|
||||||
}, function (result) {
|
|
||||||
manager.close();
|
|
||||||
callback(result);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var LOGIN_CSS =
|
|
||||||
'<style>@import url("' + CONNECT_PATH + '/login_buttons.css")</style>';
|
|
||||||
|
|
||||||
var LOGIN_ONCLICK =
|
|
||||||
'TrezorConnect.requestLogin('
|
|
||||||
+ "'@hosticon@','@challenge_hidden@','@challenge_visual@','@callback@'"
|
|
||||||
+ ')';
|
|
||||||
|
|
||||||
var LOGIN_HTML =
|
|
||||||
'<div id="trezorconnect-wrapper">'
|
|
||||||
+ ' <a id="trezorconnect-button" onclick="' + LOGIN_ONCLICK + '">'
|
|
||||||
+ ' <span id="trezorconnect-icon"></span>'
|
|
||||||
+ ' <span id="trezorconnect-text">@text@</span>'
|
|
||||||
+ ' </a>'
|
|
||||||
+ ' <span id="trezorconnect-info">'
|
|
||||||
+ ' <a id="trezorconnect-infolink" href="https://www.buytrezor.com/"'
|
|
||||||
+ ' target="_blank">What is TREZOR?</a>'
|
|
||||||
+ ' </span>'
|
|
||||||
+ '</div>';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find <trezor:login> elements and replace them with login buttons.
|
|
||||||
* It's not required to use these special elements, feel free to call
|
|
||||||
* `TrezorConnect.requestLogin` directly.
|
|
||||||
*/
|
|
||||||
this.renderLoginButtons = function () {
|
|
||||||
var elements = document.getElementsByTagName('trezor:login');
|
|
||||||
|
|
||||||
for (var i = 0; i < elements.length; i++) {
|
|
||||||
var e = elements[i];
|
|
||||||
var text = e.getAttribute('text') || 'Sign in with TREZOR';
|
|
||||||
var callback = e.getAttribute('callback') || '';
|
|
||||||
var hosticon = e.getAttribute('icon') || '';
|
|
||||||
var challenge_hidden = e.getAttribute('challenge_hidden') || '';
|
|
||||||
var challenge_visual = e.getAttribute('challenge_visual') || '';
|
|
||||||
|
|
||||||
// it's not valid to put markup into attributes, so let users
|
|
||||||
// supply a raw text and make TREZOR bold
|
|
||||||
text = text.replace('TREZOR', '<strong>TREZOR</strong>');
|
|
||||||
|
|
||||||
e.parentNode.innerHTML =
|
|
||||||
LOGIN_CSS + LOGIN_HTML
|
|
||||||
.replace('@text@', text)
|
|
||||||
.replace('@callback@', callback)
|
|
||||||
.replace('@hosticon@', hosticon)
|
|
||||||
.replace('@challenge_hidden@', challenge_hidden)
|
|
||||||
.replace('@challenge_visual@', challenge_visual);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var exports = new TrezorConnect();
|
|
||||||
exports.renderLoginButtons();
|
|
||||||
return exports;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* `getXPubKey()`
|
|
||||||
*/
|
|
||||||
|
|
||||||
function parseHDPath(string) {
|
|
||||||
return string
|
|
||||||
.toLowerCase()
|
|
||||||
.split('/')
|
|
||||||
.filter(function (p) { return p !== 'm'; })
|
|
||||||
.map(function (p) {
|
|
||||||
var n = parseInt(p);
|
|
||||||
if (p[p.length - 1] === "'") { // hardened index
|
|
||||||
n = n | 0x80000000;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Popup management
|
|
||||||
*/
|
|
||||||
|
|
||||||
function Popup(url, name, params) {
|
|
||||||
var w = window.open(url, name, params);
|
|
||||||
|
|
||||||
var interval;
|
|
||||||
var iterate = function () {
|
|
||||||
if (w.closed) {
|
|
||||||
clearInterval(interval);
|
|
||||||
if (this.onclose) {
|
|
||||||
this.onclose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.bind(this);
|
|
||||||
interval = setInterval(iterate, 100);
|
|
||||||
|
|
||||||
this.window = w;
|
|
||||||
this.onclose = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Channel(target, origin, waiting) {
|
|
||||||
|
|
||||||
var respond = function (data) {
|
|
||||||
if (waiting) {
|
|
||||||
var callback = waiting;
|
|
||||||
waiting = null;
|
|
||||||
callback(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var receive = function (event) {
|
|
||||||
if (event.source === target && event.origin === origin) {
|
|
||||||
respond(event.data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('message', receive);
|
|
||||||
|
|
||||||
this.respond = respond;
|
|
||||||
|
|
||||||
this.close = function () {
|
|
||||||
window.removeEventListener('message', receive);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.send = function (value, callback) {
|
|
||||||
console.log('[trezor.js.270:value:]',value); //TODO
|
|
||||||
if (waiting === null) {
|
|
||||||
waiting = callback;
|
|
||||||
target.postMessage(value, origin);
|
|
||||||
} else {
|
|
||||||
throw new Error(ERR_ALREADY_WAITING);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function ConnectedChannel(url, origin, name, params) {
|
|
||||||
|
|
||||||
var ready = function () {
|
|
||||||
clearTimeout(this.timeout);
|
|
||||||
this.popup.onclose = null;
|
|
||||||
this.ready = true;
|
|
||||||
this.onready();
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
var closed = function () {
|
|
||||||
clearTimeout(this.timeout);
|
|
||||||
this.channel.close();
|
|
||||||
this.onerror(new Error(ERR_WINDOW_CLOSED));
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
var timedout = function () {
|
|
||||||
this.popup.onclose = null;
|
|
||||||
this.popup.window.close();
|
|
||||||
this.channel.close();
|
|
||||||
this.onerror(new Error(ERR_TIMED_OUT));
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
this.popup = new Popup(url, name, params);
|
|
||||||
this.channel = new Channel(this.popup.window, origin, ready);
|
|
||||||
this.timeout = setTimeout(timedout, 5000);
|
|
||||||
|
|
||||||
this.popup.onclose = closed;
|
|
||||||
|
|
||||||
this.ready = false;
|
|
||||||
this.onready = null;
|
|
||||||
this.onerror = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function PopupManager(url, origin, name, onparams) {
|
|
||||||
var cc = null;
|
|
||||||
|
|
||||||
var closed = function () {
|
|
||||||
cc.channel.respond(new Error(ERR_WINDOW_CLOSED));
|
|
||||||
cc.channel.close();
|
|
||||||
cc = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
var open = function (callback) {
|
|
||||||
cc = new ConnectedChannel(url, origin, name, onparams());
|
|
||||||
cc.onready = function () {
|
|
||||||
cc.popup.onclose = closed;
|
|
||||||
callback(cc.channel);
|
|
||||||
};
|
|
||||||
cc.onerror = function (error) {
|
|
||||||
cc = null;
|
|
||||||
callback(error);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
this.close = function () {
|
|
||||||
if (cc) {
|
|
||||||
cc.popup.window.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.waitForChannel = function (callback) {
|
|
||||||
if (cc) {
|
|
||||||
if (cc.ready) {
|
|
||||||
callback(cc.channel);
|
|
||||||
} else {
|
|
||||||
callback(new Error(ERR_ALREADY_WAITING));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
open(callback);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.sendWithChannel = function (message, callback) {
|
|
||||||
var onresponse = function (response) {
|
|
||||||
if (response instanceof Error) {
|
|
||||||
callback({success: false, error: response.message});
|
|
||||||
} else {
|
|
||||||
callback(response);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var onchannel = function (channel) {
|
|
||||||
if (channel instanceof Error) {
|
|
||||||
callback({success: false, error: channel.message});
|
|
||||||
} else {
|
|
||||||
channel.send(message, onresponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.waitForChannel(onchannel);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}());
|
|
Loading…
Reference in New Issue