better wallet
This commit is contained in:
Generated
+13
@@ -3724,6 +3724,11 @@
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"emitter-component": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz",
|
||||
"integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
@@ -9138,6 +9143,14 @@
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||
},
|
||||
"stream": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz",
|
||||
"integrity": "sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8=",
|
||||
"requires": {
|
||||
"emitter-component": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"stream-browserify": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"key-file-storage": "^2.2.4",
|
||||
"node-fetch": "^2.6.0",
|
||||
"rxjs": "~6.5.2",
|
||||
"stream": "0.0.2",
|
||||
"systemjs": "^0.21.3",
|
||||
"tslib": "^1.9.0",
|
||||
"uuid": "^3.3.2",
|
||||
|
||||
@@ -41,7 +41,7 @@ declare const fb
|
||||
<clr-dg-row *clrDgItems="let line of plugins">
|
||||
<clr-dg-cell>{{line.name}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<span *ngIf="line.installed != line.available" class="label label-light-blue">{{line.available}}</span>
|
||||
<span *ngIf="line.installed != line.available" class="label info">{{line.available}}</span>
|
||||
<span *ngIf="line.installed == line.available">{{line.available}}</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{line.installed}}</clr-dg-cell>
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
import { Component, OnInit, isDevMode } from '@angular/core';
|
||||
import { PluginsComponent } from './btc.component';
|
||||
import { ClrDatagridStringFilterInterface, ClrDatagridNumericFilterInterface, ClrDropdownModule } from '@clr/angular';
|
||||
declare const fb
|
||||
@Component({
|
||||
selector: 'address-viewer', //!!!!
|
||||
template: `
|
||||
<div class="clr-row">
|
||||
<div *ngIf="regularAddresses.length != 0" class="clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-12 clr-col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Addresses
|
||||
@@ -18,25 +21,39 @@ declare const fb
|
||||
</clr-alert-item>
|
||||
</clr-alert>
|
||||
|
||||
<clr-datagrid class="datagrid-compact">
|
||||
<clr-datagrid class="datagrid-compact ">
|
||||
<clr-dg-column>Address</clr-dg-column>
|
||||
<clr-dg-column>Balance</clr-dg-column>
|
||||
<clr-dg-column>Pending</clr-dg-column>
|
||||
<clr-dg-column>Estimate total</clr-dg-column>
|
||||
<clr-dg-column>
|
||||
Balance
|
||||
</clr-dg-column>
|
||||
<clr-dg-column>
|
||||
Pending
|
||||
</clr-dg-column>
|
||||
<clr-dg-column>
|
||||
Estimate total
|
||||
<clr-dg-numeric-filter [clrDgNumericFilter]="otherBalanceFilter" [clrFilterValue]="[0, null]"></clr-dg-numeric-filter>
|
||||
</clr-dg-column>
|
||||
|
||||
<clr-dg-row *clrDgItems="let line of regularAddresses">
|
||||
<clr-dg-cell style="word-break: break-all">{{line.address}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{line.confirmed.balance}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{line.unconfirmed.balance}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{line.total.balance}}</clr-dg-cell>
|
||||
<clr-dg-cell class="monospace" style="word-break: break-all">{{line.address}}</clr-dg-cell>
|
||||
<clr-dg-cell class="monospace">{{line.confirmed.balance | number: '1.8-18'}}</clr-dg-cell>
|
||||
<clr-dg-cell class="monospace">
|
||||
<span *ngIf="line.unconfirmed.balance != 0">
|
||||
{{line.unconfirmed.balance | number: '1.8-18'}}
|
||||
</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell class="monospace">{{line.total.balance | number: '1.8-18'}}</clr-dg-cell>
|
||||
|
||||
<clr-dg-row-detail *clrIfExpanded >
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-3">
|
||||
<button class="btn btn-info-outline">Explorer</button>
|
||||
<button class="btn btn-info-outline" (click)="parent.addrExplorer(line.address)">Explorer</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="parent.keys[line.address] != null" class="clr-col-9" style="word-break:break-all">
|
||||
<h6 style="margin-top: 0"> Public key </h6>
|
||||
<h6 style="margin-top: 0">Derivation </h6>
|
||||
{{parent.keys[line.address].derivation}}
|
||||
<h6> Public key </h6>
|
||||
{{parent.keys[line.address].publicExtendedKey}}
|
||||
<h6> Private key </h6>
|
||||
{{parent.keys[line.address].privateExtendedKey}}
|
||||
@@ -46,33 +63,63 @@ declare const fb
|
||||
</clr-dg-row>
|
||||
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="5">
|
||||
<clr-dg-pagination #pagination2 [clrDgPageSize]="10">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[5,10,20,50,100]">per page</clr-dg-page-size>
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||
of {{pagination.totalItems}}
|
||||
{{pagination2.firstItem + 1}} - {{pagination2.lastItem + 1}}
|
||||
of {{pagination2.totalItems}}
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="changeAddresses.length != 0" class="clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-12 clr-col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Change Addresses
|
||||
</div>
|
||||
<clr-alert *ngFor="let entry of alerts" [clrAlertType]="line.severity">
|
||||
<clr-alert-item>
|
||||
<span class="alert-text">
|
||||
{{line.message}}
|
||||
</span>
|
||||
</clr-alert-item>
|
||||
</clr-alert>
|
||||
|
||||
<clr-datagrid class="datagrid-compact">
|
||||
<clr-dg-column>Address</clr-dg-column>
|
||||
<clr-dg-column>Balance</clr-dg-column>
|
||||
<clr-dg-column>Pending</clr-dg-column>
|
||||
<clr-dg-column>Estimate total</clr-dg-column>
|
||||
<clr-dg-column>
|
||||
Balance
|
||||
</clr-dg-column>
|
||||
<clr-dg-column>
|
||||
Pending
|
||||
</clr-dg-column>
|
||||
<clr-dg-column>
|
||||
Estimate total
|
||||
<clr-dg-numeric-filter [clrDgNumericFilter]="balanceFilter" [clrFilterValue]="[0.00000001, null]"></clr-dg-numeric-filter>
|
||||
</clr-dg-column>
|
||||
|
||||
<clr-dg-row *clrDgItems="let line of changeAddresses">
|
||||
<clr-dg-cell style="word-break: break-all">{{line.address}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{line.confirmed.balance}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{line.unconfirmed.balance}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{line.total.balance}}</clr-dg-cell>
|
||||
<clr-dg-cell class="monospace" style="word-break: break-all">{{line.address}}</clr-dg-cell>
|
||||
<clr-dg-cell class="monospace">{{line.confirmed.balance | number: '1.8-18'}}</clr-dg-cell>
|
||||
<clr-dg-cell class="monospace">
|
||||
<span *ngIf="line.unconfirmed.balance != 0">
|
||||
{{line.unconfirmed.balance | number: '1.8-18'}}
|
||||
</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell class="monospace">{{line.total.balance | number: '1.8-18'}}</clr-dg-cell>
|
||||
|
||||
<clr-dg-row-detail *clrIfExpanded >
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-3">
|
||||
<button class="btn btn-info-outline">Explorer</button>
|
||||
<button class="btn btn-info-outline" (click)="parent.addrExplorer(line.address)">Explorer</button>
|
||||
<p>
|
||||
</div>
|
||||
|
||||
<div *ngIf="parent.keys[line.address] != null" class="clr-col-9" style="word-break:break-all">
|
||||
<h6 style="margin-top: 0"> Public key </h6>
|
||||
<h6 style="margin-top: 0">Derivation </h6>
|
||||
{{parent.keys[line.address].derivation}}
|
||||
<h6> Public key </h6>
|
||||
{{parent.keys[line.address].publicExtendedKey}}
|
||||
<h6> Private key </h6>
|
||||
{{parent.keys[line.address].privateExtendedKey}}
|
||||
@@ -82,7 +129,7 @@ declare const fb
|
||||
</clr-dg-row>
|
||||
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="5">
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[5,10,20,50,100]">per page</clr-dg-page-size>
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||
of {{pagination.totalItems}}
|
||||
@@ -90,10 +137,13 @@ declare const fb
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class AddressViewerComponent implements OnInit {
|
||||
|
||||
export class AddressViewerComponent implements OnInit{
|
||||
balanceFilter = new BalanceFilter()
|
||||
otherBalanceFilter = new BalanceFilter()
|
||||
regularAddresses = []
|
||||
changeAddresses = []
|
||||
keys = {}
|
||||
@@ -113,3 +163,12 @@ export class AddressViewerComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
class BalanceFilter implements ClrDatagridNumericFilterInterface<any>{
|
||||
accepts(item: any, low: number, high: number): boolean {
|
||||
high = parseFloat(""+high)
|
||||
low = parseFloat(""+low)
|
||||
const balance = (parseFloat(item.unconfirmed.balance) + parseFloat(item.confirmed.balance))
|
||||
return (Number.isNaN(low) || balance >= low)
|
||||
&& (Number.isNaN(high) || balance <= high)
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { HDKey } from "btc-hdkey"
|
||||
import { AddressViewerComponent } from './address-viewer.component';
|
||||
const fetch = require("node-fetch")
|
||||
const coinselect = require('coinselect/accumulative')
|
||||
import { TransactionBuilder } from 'bitcoinjs-lib'
|
||||
import { TransactionBuilder, Signer } from 'bitcoinjs-lib'
|
||||
import { networks } from 'bitcoinjs-lib'
|
||||
let feeRate = 55 // satoshis per byte
|
||||
|
||||
@@ -17,8 +17,43 @@ declare const fb
|
||||
|
||||
<wallet-generator></wallet-generator>
|
||||
|
||||
<!-- loading -->
|
||||
<clr-modal [clrModalSize]="'md'" [(clrModalOpen)]="loading">
|
||||
|
||||
<div class="modal-body ">
|
||||
<span class="spinner spinner-inverse spinner-md">
|
||||
Loading...
|
||||
</span>
|
||||
<h3>Checking addresses</h3> <br>
|
||||
Checking the first {{loadingN*115}} addresses. <br>
|
||||
Found addresses containing balance: {{regaddresses.length + changeaddresses.length}}
|
||||
</div>
|
||||
</clr-modal>
|
||||
<!-- / loading -->
|
||||
|
||||
<!-- row 0 -->
|
||||
<div *ngIf="mode.net == 'testnet'" class="clr-all-12">
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header">
|
||||
Faucet
|
||||
</div>
|
||||
<div class="card-block" >
|
||||
<button class="btn btn-icon btn-info-outline" onclick="window.open('https://testnet-faucet.mempool.co/', '_blank')"><clr-icon shape="fuel"></clr-icon></button>
|
||||
<button class="btn btn-icon btn-info-outline" onclick="window.open('https://bitcoinfaucet.uo1.net/', '_blank')"><clr-icon shape="fuel"></clr-icon></button>
|
||||
<button class="btn btn-icon btn-info-outline" onclick="window.open('https://coinfaucet.eu/en/btc-testnet/', '_blank')"><clr-icon shape="fuel"></clr-icon></button>
|
||||
<button class="btn btn-icon btn-success-outline" onclick="window.open('https://www.google.com/search?q=btc+testnet+faucet&oq=btc+testnet+faucet', '_blank')"><clr-icon shape="plus"></clr-icon> more</button>
|
||||
</div>
|
||||
<div class="card-footer" >
|
||||
Get free test-BTC
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- row 1 -->
|
||||
<div class="clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-12 clr-col-xl-12">
|
||||
<div class="clr-all-12">
|
||||
|
||||
<!-- wallet picker + send box + balance box -->
|
||||
<div class="clr-row">
|
||||
@@ -28,26 +63,42 @@ declare const fb
|
||||
|
||||
<!-- Wallet picker -->
|
||||
<div class="clr-row">
|
||||
<walletpicker class="clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-12 clr-col-xl-12"></walletpicker>
|
||||
<walletpicker class="clr-all-12"></walletpicker>
|
||||
</div>
|
||||
<!-- /Wallet picker -->
|
||||
|
||||
<!-- Send box -->
|
||||
<div class="clr-row">
|
||||
<div *ngIf="mode.type=='private'" class="clr-row">
|
||||
|
||||
<div class="clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-12 clr-col-xl-12">
|
||||
<div class="clr-all-12">
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header">
|
||||
Send
|
||||
</div>
|
||||
|
||||
<clr-alert *ngFor="let error of errors" [clrAlertType]="'danger'">
|
||||
<clr-alert-item>
|
||||
<span class="alert-text">
|
||||
{{error}}
|
||||
</span>
|
||||
</clr-alert-item>
|
||||
</clr-alert>
|
||||
<clr-alert *ngFor="let success of successes" [clrAlertType]="'success'">
|
||||
<clr-alert-item>
|
||||
<span class="alert-text">
|
||||
{{success}}
|
||||
</span>
|
||||
</clr-alert-item>
|
||||
</clr-alert>
|
||||
<div class="card-block" >
|
||||
<input [(ngModel)]="sendTo" type="text" placeholder="To address" clrInput />
|
||||
<input [(ngModel)]="sendAmount" type="number" min="0" placeholder="Amount" clrInput />
|
||||
|
||||
<div class="clr-row" style="padding-left:12px; padding-right:12px">
|
||||
<input [(ngModel)]="sendTo" type="text" placeholder="To address" class="clr-input clr-col-12" />
|
||||
<input [(ngModel)]="sendAmount" type="text" min="0" placeholder="Amount" class="clr-input clr-col-12" style="margin-top:12px; margin-borrom:12px" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer" >
|
||||
<button class="btn btn-success-outline" (click)="createTx(sendTo, sendAmount)">send</button>
|
||||
<button class="btn btn-success-outline" (click)="createAndSendTx(sendTo, sendAmount)">send</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -63,9 +114,9 @@ declare const fb
|
||||
<div class="clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-7 clr-col-xl-7" >
|
||||
|
||||
<!-- Balance box -->
|
||||
<div class="card">
|
||||
<div *ngIf="rootkey.publicExtendedKey != ''" class="card">
|
||||
<div class="card-header" style="word-break: break-all">
|
||||
{{rootkey.publicExtendedKey}}
|
||||
<span *ngIf="mode.net=='testnet'" class="label label-danger">Testnet</span>{{rootkey.publicExtendedKey}}
|
||||
</div>
|
||||
<clr-alert *ngFor="let entry of alerts" [clrAlertType]="line.severity">
|
||||
<clr-alert-item>
|
||||
@@ -75,12 +126,19 @@ declare const fb
|
||||
</clr-alert-item>
|
||||
</clr-alert>
|
||||
|
||||
<div class="card-block" >
|
||||
<h3 style="margin-top:12px">Balance: {{balance.confirmed}}</h3>
|
||||
<p>
|
||||
Unconfirmed: {{balance.unconfirmed}}<br />
|
||||
Estimated total: {{balance.total}}
|
||||
<div class="card-block">
|
||||
<h3 style="margin-top:12px">{{balanceDisplay.total | number: '1.8-18'}} BTC </h3>
|
||||
<p class="monospace">
|
||||
<span *ngIf="balanceDisplay.unconfirmed < 0"> </span>{{balanceDisplay.confirmed | number: '1.18'}} BTC confirmed<br />
|
||||
{{balanceDisplay.unconfirmed | number: '1.18'}} BTC pending
|
||||
</p>
|
||||
<p *ngIf="mode.type=='private'">
|
||||
<clr-icon shape="key"></clr-icon> Private key available
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card-footer" >
|
||||
<button class="btn btn-icon" (click)="reload()"> <clr-icon shape="refresh"></clr-icon> Reload </button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Balance box-->
|
||||
@@ -91,50 +149,39 @@ declare const fb
|
||||
</div>
|
||||
<!-- /row wallet picker + send box + balance box -->
|
||||
|
||||
<div class="clr-col-12 clr-col-sm-12 clr-col-md-12 clr-col-lg-12 clr-col-xl-12" style="padding-left:0px; padding-right: 0px">
|
||||
<div class="clr-all-12" style="padding-left:0px; padding-right: 0px">
|
||||
<address-viewer></address-viewer>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /row 1 -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
`
|
||||
})
|
||||
export class PluginsComponent implements AfterViewInit {
|
||||
|
||||
wallets:{
|
||||
[someLabel in string]:{
|
||||
key:string
|
||||
name:string
|
||||
}
|
||||
} = {}
|
||||
errors:string[] = []
|
||||
successes:string[] = []
|
||||
wallets:WalletStore = {}
|
||||
txToSend:any
|
||||
rootkey = {
|
||||
publicExtendedKey: "",
|
||||
privateExtendedKey: ""
|
||||
}
|
||||
keys = {}
|
||||
regaddresses = []
|
||||
changeaddresses = []
|
||||
balance = {
|
||||
rootkey:bip32Keypair = {publicExtendedKey: "",privateExtendedKey: ""}
|
||||
keys:KeyStore = {}
|
||||
regaddresses:AddressLine[] = []
|
||||
changeaddresses:AddressLine[] = []
|
||||
balanceDisplay = {
|
||||
confirmed: 0,
|
||||
total: 0,
|
||||
unconfirmed: 0
|
||||
}
|
||||
mode : {
|
||||
net:network
|
||||
type:keyType,
|
||||
net:networkType
|
||||
} = {
|
||||
net: "testnet"
|
||||
net: "testnet",
|
||||
type: "public"
|
||||
}
|
||||
sendto:string
|
||||
amount:number
|
||||
sendTo:string
|
||||
sendAmount:number
|
||||
loading = false
|
||||
loadingN = 1
|
||||
|
||||
constructor(){
|
||||
window["btc"] = () => this
|
||||
}
|
||||
@ViewChild(AddressViewerComponent, {static: false})
|
||||
AddressViewerComponent:AddressViewerComponent
|
||||
|
||||
@@ -149,8 +196,8 @@ export class PluginsComponent implements AfterViewInit {
|
||||
this.WalletPickerComponent.setParent(this)
|
||||
this.AddressViewerComponent.setParent(this)
|
||||
|
||||
//tprv8cXm1S7PGS2fHuLjZajuDZZsvxerzv4zZJJAhkLAWZzwz5rE7efHm31xsqpzfoGMHBNC8bkRcsjiMdmkSuNn5C1qNqk7rrk1pCuhk4HfVak
|
||||
//tpubD9Do9r9dQoiLBNNXTEQVcyDzVzAoAFFu8btwzGNTvqoLpa6zk3UswXdq3zeArrakUu8huCtG1L5pTqNYNftanuutf57fn1C74rBzFybH1xe
|
||||
window['btc'] = this
|
||||
|
||||
this.loadKey("tprv8cXm1S7PGS2fHuLjZajuDZZsvxerzv4zZJJAhkLAWZzwz5rE7efHm31xsqpzfoGMHBNC8bkRcsjiMdmkSuNn5C1qNqk7rrk1pCuhk4HfVak")
|
||||
}
|
||||
|
||||
@@ -169,50 +216,46 @@ export class PluginsComponent implements AfterViewInit {
|
||||
loadKey(key:string){
|
||||
this.wallets = {}
|
||||
this.txToSend = null
|
||||
this.sendto = ""
|
||||
this.sendTo = ""
|
||||
this.regaddresses = []
|
||||
|
||||
const t = hdkey.checkHDKey(key)
|
||||
if(t){
|
||||
console.log(t)
|
||||
this.mode = t
|
||||
}
|
||||
|
||||
this.rootkey = hdkey.HDKeyFromExtendedKey(key)
|
||||
this.refresh()
|
||||
this.reload()
|
||||
}
|
||||
|
||||
async refresh(){
|
||||
async reload():Promise<void>{
|
||||
this.loading = true
|
||||
this.loadingN = 1
|
||||
this.keys = {}
|
||||
this.changeaddresses = []
|
||||
this.regaddresses = []
|
||||
this.AddressViewerComponent.setChangeAddresses([])
|
||||
this.AddressViewerComponent.setRegularAddresses([])
|
||||
this.balance.total = 0
|
||||
this.balance.unconfirmed = 0
|
||||
this.balance.confirmed = 0
|
||||
this.balanceDisplay.total = 0
|
||||
this.balanceDisplay.unconfirmed = 0
|
||||
this.balanceDisplay.confirmed = 0
|
||||
await this.refresh()
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
async refresh():Promise<void>{
|
||||
let p1 = this.updateChange()
|
||||
let p2 = this.update()
|
||||
let p2 = this.updateBalance()
|
||||
|
||||
let balances:any[] = await Promise.all([p1,p2])
|
||||
let balances:GrandTotal[] = await Promise.all([p1,p2])
|
||||
balances.forEach(balance => {
|
||||
this.balance.confirmed += balance.confirmed
|
||||
this.balance.total += balance.total
|
||||
this.balance.unconfirmed += balance.unconfirmed
|
||||
this.balanceDisplay.confirmed += balance.confirmed
|
||||
this.balanceDisplay.total += balance.total
|
||||
this.balanceDisplay.unconfirmed += balance.unconfirmed
|
||||
})
|
||||
|
||||
console.log(this.changeaddresses)
|
||||
|
||||
this.AddressViewerComponent.setChangeAddresses(this.changeaddresses)
|
||||
this.AddressViewerComponent.setRegularAddresses(this.regaddresses)
|
||||
}
|
||||
|
||||
|
||||
async getInfo(rootAddr: string, n = 10, skip = 0, changeAddresses = false): Promise<any>{
|
||||
const t = hdkey.checkHDKey(rootAddr)
|
||||
if(!t){
|
||||
return false
|
||||
}
|
||||
getAddresses(rootAddr: string, n = 10, skip = 0, changeAddresses = false): string[] | null{
|
||||
let addresses: string[] = []
|
||||
for(let i = skip; i < skip+n; i++){
|
||||
let addr
|
||||
@@ -222,13 +265,17 @@ export class PluginsComponent implements AfterViewInit {
|
||||
addr = hdkey.deriveAddress(rootAddr, i)
|
||||
}
|
||||
if(!addr){
|
||||
return false
|
||||
return
|
||||
}
|
||||
addresses.push(addr)
|
||||
}
|
||||
return addresses
|
||||
}
|
||||
|
||||
async fetchAddresses(addresses:string[], network:networkType): Promise<{success:boolean, address?:any, addresses?:any}>{
|
||||
let lookupstr = addresses.join(',')
|
||||
try{
|
||||
let r = await fetch(api[t.net]+'/address/'+lookupstr, {
|
||||
let r = await fetch(api[network]+'/address/'+lookupstr, {
|
||||
'method':'GET',
|
||||
'mode':'cors',
|
||||
'data': {limit: 200}
|
||||
@@ -244,7 +291,18 @@ export class PluginsComponent implements AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
async getTxs(address: string, type:network){
|
||||
async getAddressInfos(rootAddr: string, n = 10, skip = 0, changeAddresses = false): Promise<any>{
|
||||
const t = hdkey.checkHDKey(rootAddr)
|
||||
if(!t){
|
||||
return false
|
||||
}
|
||||
const addresses = this.getAddresses(rootAddr, n, skip, changeAddresses)
|
||||
return await this.fetchAddresses(addresses, t.net)
|
||||
|
||||
}
|
||||
|
||||
async getTxs(address: string, type:networkType){
|
||||
try{
|
||||
let r = await fetch(api[type]+'/address/'+address, {
|
||||
'method':'GET',
|
||||
'mode':'cors'
|
||||
@@ -254,10 +312,14 @@ export class PluginsComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
return (await r).json()
|
||||
}catch(e){
|
||||
console.warn ("getTxs", e)
|
||||
}
|
||||
}
|
||||
|
||||
async getUnspentTxs(addresses:string[], type:network):Promise<any>{
|
||||
async getUnspentTxs(addresses:string[], type:networkType):Promise<any>{
|
||||
let lookupstr = addresses.join(',')
|
||||
|
||||
try{
|
||||
let r = await fetch(api[type]+'/address/'+lookupstr+'/unspent', {
|
||||
'method':'GET',
|
||||
@@ -269,6 +331,7 @@ export class PluginsComponent implements AfterViewInit {
|
||||
|
||||
return (await r).json()
|
||||
}catch(e){
|
||||
console.warn ("getUnspentTxs", e)
|
||||
return {success: false}
|
||||
}
|
||||
}
|
||||
@@ -278,30 +341,51 @@ export class PluginsComponent implements AfterViewInit {
|
||||
return this.keys[keyName]
|
||||
}
|
||||
|
||||
async update(){
|
||||
let n = 1
|
||||
let regAddresses
|
||||
calculateTotal(addresses:AddressLine[]):GrandTotal{
|
||||
const resMap: any = addresses.reduce((o1, o2)=>{o1[o2.address] = o2; return o1}, {})
|
||||
const total: any = Object.values(resMap).reduce((accum:GrandTotal, obj:AddressLine) => {
|
||||
accum.confirmed += parseFloat(<any>obj.confirmed.balance)
|
||||
accum.unconfirmed += parseFloat(<any>obj.unconfirmed.balance)
|
||||
accum.total += parseFloat(<any>obj.confirmed.balance) + parseFloat(<any>obj.unconfirmed.balance)
|
||||
return accum
|
||||
},{
|
||||
confirmed: 0,
|
||||
unconfirmed: 0,
|
||||
total: 0
|
||||
})
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
async updateBalance():Promise<GrandTotal>{
|
||||
let n = 1
|
||||
let res: { success: boolean; addresses: any; address: any; }
|
||||
this.regaddresses = []
|
||||
while(true){
|
||||
this.loadingN = n
|
||||
try{
|
||||
regAddresses = await this.getInfo(this.rootkey.publicExtendedKey, 115*n, 115*(n-1))
|
||||
res = await this.getAddressInfos(this.rootkey.publicExtendedKey, 115*n, 115*(n-1))
|
||||
}catch(e){
|
||||
break
|
||||
}
|
||||
if(regAddresses.success === true){
|
||||
if('addresses' in regAddresses){ // multiple
|
||||
this.regaddresses = [...this.regaddresses, ...regAddresses.addresses]
|
||||
if(res.success === true){
|
||||
if('addresses' in res){ // multiple
|
||||
this.regaddresses = [...this.regaddresses, ...res.addresses]
|
||||
}
|
||||
if('address' in regAddresses){
|
||||
this.regaddresses.push(regAddresses.address)
|
||||
if('address' in res){
|
||||
this.regaddresses.push(res.address)
|
||||
}
|
||||
n++
|
||||
}else{
|
||||
console.warn(`This error is a result of a failed fetch request.
|
||||
These are always shown by the browser and cannot be hidden (a design decision)
|
||||
They are completely safe and mean no more addresses were found`)
|
||||
console.log('No more balance found after attempting ', 115*n, 'addresses')
|
||||
if(this.rootkey.privateExtendedKey != null){
|
||||
for(let i = 0; i < 115*n; i++){
|
||||
if(this.mode.type == "private"){
|
||||
for(let i = 0; i < 115*n+1; i++){
|
||||
const keyN = hdkey.deriveKey(this.rootkey.privateExtendedKey, i)
|
||||
const addrN = hdkey.addressFromHDKey(keyN._publicKey, this.mode.net)
|
||||
keyN.derivation = i
|
||||
this.keys[addrN] = keyN
|
||||
}
|
||||
}
|
||||
@@ -309,46 +393,25 @@ export class PluginsComponent implements AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
if(this.regaddresses.length === 0){
|
||||
const addr = hdkey.deriveAddress(this.rootkey.publicExtendedKey, 0)
|
||||
//add an extra empty address at the end
|
||||
const extraAddr = hdkey.deriveAddress(this.rootkey.publicExtendedKey, this.regaddresses.length)
|
||||
this.regaddresses.push({
|
||||
address: addr,
|
||||
address: <string>extraAddr,
|
||||
confirmed: {balance: 0},
|
||||
unconfirmed: {balance: 0},
|
||||
total: {balance: 0},
|
||||
})
|
||||
return {
|
||||
total: 0,
|
||||
unconfirmed: 0,
|
||||
confirmed: 0
|
||||
}
|
||||
}
|
||||
const resMap: any = {}
|
||||
this.regaddresses.forEach(a => {
|
||||
resMap[a.address] = a
|
||||
})
|
||||
|
||||
const confirmed = <number>Object.values(resMap).reduce((accum:any, obj:any) => {
|
||||
return accum + parseFloat(obj.confirmed.balance)
|
||||
}, 0)
|
||||
const unconfirmed = <number>Object.values(resMap).reduce((accum:any, obj:any) => {
|
||||
return accum + parseFloat(obj.unconfirmed.balance)
|
||||
}, 0)
|
||||
|
||||
return {
|
||||
total: confirmed+unconfirmed,
|
||||
unconfirmed: unconfirmed,
|
||||
confirmed: confirmed
|
||||
}
|
||||
return this.calculateTotal(this.regaddresses)
|
||||
}
|
||||
|
||||
async updateChange(){
|
||||
async updateChange():Promise<GrandTotal>{
|
||||
let n = 1
|
||||
let res
|
||||
|
||||
this.changeaddresses = []
|
||||
while(true){
|
||||
let res
|
||||
try{
|
||||
res = await this.getInfo(this.rootkey.publicExtendedKey, 115*n, 115*(n-1), true)
|
||||
res = await this.getAddressInfos(this.rootkey.publicExtendedKey, 115*n, 115*(n-1), true)
|
||||
}catch(e){
|
||||
break
|
||||
}
|
||||
@@ -361,11 +424,16 @@ export class PluginsComponent implements AfterViewInit {
|
||||
}
|
||||
n++
|
||||
}else{
|
||||
console.log('No more balance found after attempting ', 115*n, 'addresses')
|
||||
if(this.rootkey.privateExtendedKey != null){
|
||||
console.warn(`This error is a result of a failed fetch request.
|
||||
These are always shown by the browser and cannot be hidden (a design decision)
|
||||
They are completely safe and mean no more addresses were found`)
|
||||
console.log('No more change balance found after attempting ', 115*n, 'addresses')
|
||||
if(this.mode.type == "private"){
|
||||
//make keypairs for addresses
|
||||
for(let i = 0; i < 115*n; i++){
|
||||
const keyN = hdkey.deriveKey(this.rootkey.privateExtendedKey, i, 'm/1/')
|
||||
const addrN = hdkey.addressFromHDKey(keyN._publicKey, this.mode.net)
|
||||
keyN.derivation = i
|
||||
this.keys[addrN] = keyN
|
||||
}
|
||||
}
|
||||
@@ -373,60 +441,26 @@ export class PluginsComponent implements AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
if(this.changeaddresses.length === 0){
|
||||
const addr = hdkey.deriveAddress(this.rootkey.publicExtendedKey, 0, 'm/1/')
|
||||
this.changeaddresses.push({
|
||||
address: addr,
|
||||
confirmed: {balance: 0},
|
||||
unconfirmed: {balance: 0},
|
||||
total: {balance: 0},
|
||||
})
|
||||
return {
|
||||
total: 0,
|
||||
unconfirmed: 0,
|
||||
confirmed: 0
|
||||
}
|
||||
return this.calculateTotal(this.changeaddresses)
|
||||
}
|
||||
|
||||
const resMap: any = {}
|
||||
this.changeaddresses.forEach(a => {
|
||||
resMap[a.address] = a
|
||||
})
|
||||
|
||||
const confirmed = <number>Object.values(resMap).reduce((accum:any, obj:any) => {
|
||||
return accum + parseFloat(obj.confirmed.balance)
|
||||
}, 0)
|
||||
const unconfirmed = <number>Object.values(resMap).reduce((accum:any, obj:any) => {
|
||||
return accum + parseFloat(obj.unconfirmed.balance)
|
||||
}, 0)
|
||||
|
||||
return {
|
||||
confirmed: confirmed,
|
||||
unconfirmed: unconfirmed,
|
||||
total: unconfirmed + confirmed
|
||||
}
|
||||
}
|
||||
|
||||
async createTx(target:string, value:number){
|
||||
|
||||
console.log(target, value)
|
||||
async createAndSendTx(target:string, value:number){
|
||||
|
||||
if(target == null || typeof target !== 'string' || target === '' || value == null || value <= 0 ){
|
||||
//this.error = 'Bad inputs. Expected bitcoin address and number'
|
||||
this.errors.push('Bad inputs. Expected bitcoin address and number')
|
||||
return
|
||||
}
|
||||
|
||||
if(value > this.balance.confirmed){
|
||||
//this.error = 'Insufficient funds'
|
||||
if(value > this.balanceDisplay.confirmed){
|
||||
this.errors.push('Insufficient funds')
|
||||
return
|
||||
}
|
||||
|
||||
let addresses: string[] = this.regaddresses.map(a => a.address)
|
||||
addresses = [...addresses, ...this.changeaddresses.map(a => a.address)]
|
||||
let addresses: string[] = [...this.regaddresses.map(a => a.address), ...this.changeaddresses.map(a => a.address)]
|
||||
const txs = await this.getUnspentTxs(addresses, this.mode.net)
|
||||
|
||||
if(txs == null || txs.success !== true || txs.unspent.length === 0){
|
||||
//this.error = 'No UTXOs found'
|
||||
this.errors.push('No UTXOs found')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -443,7 +477,7 @@ export class PluginsComponent implements AfterViewInit {
|
||||
|
||||
// .inputs and .outputs will be undefined if no solution was found
|
||||
if (!inputs || !outputs){
|
||||
//this.error = 'Could not build a valid transaction'
|
||||
this.errors.push('Could not build a valid transaction')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -461,7 +495,7 @@ export class PluginsComponent implements AfterViewInit {
|
||||
txb.addOutput(output.address, output.value)
|
||||
})
|
||||
inputs.forEach((input, i) => {
|
||||
txb.sign(i, this.keys[input.address])
|
||||
txb.sign(i, <Signer>this.keys[input.address])
|
||||
})
|
||||
|
||||
this.txToSend = txb.build()
|
||||
@@ -469,37 +503,61 @@ export class PluginsComponent implements AfterViewInit {
|
||||
this.sendTx()
|
||||
}
|
||||
|
||||
async sendTx(){
|
||||
private async sendTx(){
|
||||
if(this.txToSend == null){
|
||||
//this.error = 'No transaction to send!'
|
||||
this.errors.push('No transaction to send!')
|
||||
return
|
||||
}
|
||||
|
||||
let url
|
||||
if(this.mode.net === 'mainnet'){
|
||||
url = 'https://api.smartbit.com.au/v1/blockchain/pushtx'
|
||||
}else{
|
||||
url = 'https://testnet-api.smartbit.com.au/v1/blockchain/pushtx'
|
||||
}
|
||||
let r = await fetch(url, {
|
||||
let url = api[this.mode.net]+"/pushtx"
|
||||
let r
|
||||
try{
|
||||
r = await fetch(url, {
|
||||
'method':'POST',
|
||||
'body': JSON.stringify({'hex': this.txToSend.toHex()})
|
||||
})
|
||||
}catch(e){
|
||||
console.warn("sendTx", e)
|
||||
return
|
||||
}
|
||||
this.txToSend = null
|
||||
if(r.status === 200){
|
||||
console.log(r)
|
||||
this.refresh()
|
||||
this.errors = []
|
||||
this.successes.push("Sent "+this.sendAmount+" to "+this.sendTo)
|
||||
this.reload()
|
||||
}else{
|
||||
//this.error = 'The API '+url+' returned a non-200 error code '+r.status+' '+r.statusText
|
||||
console.log(r)
|
||||
this.errors.push('The API '+url+' returned a non-200 error code '+r.status+' '+r.statusText)
|
||||
}
|
||||
}
|
||||
|
||||
addrExplorer(address){
|
||||
const url = this.mode.net === 'testnet'?'https://live.blockcypher.com/btc-testnet/address/':'https://live.blockcypher.com/btc/address/'
|
||||
window.open(url+address, '_blank')
|
||||
}
|
||||
}
|
||||
|
||||
export type KeyStore = {[keyname in string]: bip32Keypair & Signer}
|
||||
|
||||
type network = 'testnet' | 'mainnet'
|
||||
export type AddressLine = {
|
||||
address: string,
|
||||
confirmed: {balance: number},
|
||||
unconfirmed: {balance: number},
|
||||
total: {balance: number},
|
||||
}
|
||||
|
||||
type networkType = 'testnet' | 'mainnet'
|
||||
type keyType = 'public' | 'private'
|
||||
|
||||
type WalletStore = {
|
||||
[address in string]:{
|
||||
key:string
|
||||
name:string
|
||||
}
|
||||
}
|
||||
type GrandTotal = {
|
||||
confirmed: number,
|
||||
unconfirmed: number,
|
||||
total: number
|
||||
}
|
||||
type bip32Keypair = { publicExtendedKey: string, privateExtendedKey: string }
|
||||
|
||||
const api = {
|
||||
|
||||
@@ -142,6 +142,7 @@ declare const fb
|
||||
</clr-checkbox-wrapper>
|
||||
<ng-template clrPageButtons>
|
||||
<clr-wizard-button [type]="'cancel'">Cancel</clr-wizard-button>
|
||||
<clr-wizard-button [type]="'back'">Back</clr-wizard-button>
|
||||
<clr-wizard-button class="btn btn-success" [type]="'myfinish'">Finish</clr-wizard-button>
|
||||
</ng-template>
|
||||
</clr-wizard-page>
|
||||
|
||||
@@ -7,7 +7,7 @@ declare const fb
|
||||
selector: 'walletpicker', //!!!!
|
||||
|
||||
template: `
|
||||
<div class="card" style="overflow:hidden">
|
||||
<div class="card" style="overflow:hidden; padding-bottom:12px">
|
||||
<div class="card-header">
|
||||
Wallets
|
||||
</div>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
.heightmax{
|
||||
height: 100%
|
||||
}
|
||||
.break-word {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.monospace {
|
||||
font-family: monospace;
|
||||
font-size: 90%
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
<div style="overflow:hidden" class="content-area clr-col-12 clr-col-sm-12 clr-col-lg-12 clr-col-xl-12">
|
||||
<router-outlet></router-outlet>
|
||||
<div class="content-area" style="max-height: 100%">
|
||||
<router-outlet class="clr-all-12" ></router-outlet>
|
||||
</div>
|
||||
|
||||
@@ -1 +1,52 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@import "../node_modules/@clr/ui/src/utils/components.clarity";
|
||||
|
||||
.heightmax{
|
||||
height: 100%
|
||||
}
|
||||
.break-word {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.monospace {
|
||||
font-family: monospace;
|
||||
font-size: 90%
|
||||
}
|
||||
*::-webkit-scrollbar-track
|
||||
{
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: rgba(0,0,0,0);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar
|
||||
{
|
||||
width: 7px;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb
|
||||
{
|
||||
border-radius: 7px;
|
||||
background-color: #0079B8;
|
||||
}
|
||||
|
||||
.clr-all-12{
|
||||
@extend .clr-col-12, .clr-col-sm-12, .clr-col-md-12, .clr-col-lg-12, .clr-col-xl-12
|
||||
}
|
||||
|
||||
.clr-all-4{
|
||||
@extend .clr-col-4, .clr-col-sm-4, .clr-col-md-4, .clr-col-lg-4, .clr-col-xl-4
|
||||
}
|
||||
|
||||
.clr-all-6{
|
||||
@extend .clr-col-6, .clr-col-sm-6, .clr-col-md-6, .clr-col-lg-6, .clr-col-xl-6
|
||||
}
|
||||
|
||||
.clr-all-3{
|
||||
@extend .clr-col-3, .clr-col-sm-3, .clr-col-md-3, .clr-col-lg-3, .clr-col-xl-3
|
||||
}
|
||||
|
||||
.clr-all-2{
|
||||
@extend .clr-col-2, .clr-col-sm-2, .clr-col-md-2, .clr-col-lg-2, .clr-col-xl-2
|
||||
}
|
||||
Reference in New Issue
Block a user