diff --git a/src/app/app.component.html b/src/app/app.component.html
index 0c2aa066..6b798028 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -34,7 +34,7 @@
-
+
Pending
{{ wallet.pendingFiat | fiat: settings.settings.displayCurrency }}
{{ wallet.pending | rai: settings.settings.displayDenomination }}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 25184451..f17c4ac3 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -49,8 +49,13 @@ export class AppComponent implements OnInit {
async ngOnInit() {
this.windowHeight = window.innerHeight;
this.settings.loadAppSettings();
+
+ // New for v19: Patch saved xrb_ prefixes to nano_
+ await this.patchXrbToNanoPrefixData();
+
this.addressBook.loadAddressBook();
this.workPool.loadWorkCache();
+
await this.walletService.loadStoredWallet();
this.websocket.connect();
@@ -107,6 +112,21 @@ export class AppComponent implements OnInit {
}
+ /*
+ This is important as it looks through saved data using hardcoded xrb_ prefixes
+ (Your wallet, address book, rep list, etc) and updates them to nano_ prefix for v19 RPC
+ */
+ async patchXrbToNanoPrefixData() {
+ // If wallet is version 2, data has already been patched. Otherwise, patch all data
+ if (this.settings.settings.walletVersion >= 2) return;
+
+ await this.walletService.patchOldSavedData(); // Change saved xrb_ addresses to nano_
+ this.addressBook.patchXrbPrefixData();
+ this.representative.patchXrbPrefixData();
+
+ this.settings.setAppSetting('walletVersion', 2); // Update wallet version so we do not patch in the future.
+ }
+
toggleSearch(mobile = false) {
this.showSearchBar = !this.showSearchBar;
if (this.showSearchBar) {
@@ -118,7 +138,7 @@ export class AppComponent implements OnInit {
const searchData = this.searchData.trim();
if (!searchData.length) return;
- if (searchData.startsWith('xrb_')) {
+ if (searchData.startsWith('xrb_') || searchData.startsWith('nano_')) {
this.router.navigate(['account', searchData]);
} else if (searchData.length === 64) {
this.router.navigate(['transaction', searchData]);
diff --git a/src/app/components/send/send.component.html b/src/app/components/send/send.component.html
index 2e02167d..43cb631b 100644
--- a/src/app/components/send/send.component.html
+++ b/src/app/components/send/send.component.html
@@ -26,7 +26,7 @@
Send Nano
-
+
@@ -184,6 +184,18 @@ Send Nano
+
+
+
+
+ You are sending
+ {{ rawAmount | rai: 'mnano' }}
+ +{{ amountRaw.toString(10) }} raw
+ {{ amountFiat | fiat: settings.settings.displayCurrency }} @ {{ price.price.lastPrice | fiat: settings.settings.displayCurrency }} / NANO
+
+
+
+
diff --git a/src/app/components/send/send.component.ts b/src/app/components/send/send.component.ts
index 8d16632f..3d25fbd6 100644
--- a/src/app/components/send/send.component.ts
+++ b/src/app/components/send/send.component.ts
@@ -188,7 +188,7 @@ export class SendComponent implements OnInit {
if (this.amount < 0 || rawAmount.lessThan(0)) return this.notificationService.sendWarning(`Amount is invalid`);
if (nanoAmount.lessThan(1)) return this.notificationService.sendWarning(`Transactions for less than 1 nano will be ignored by the node. Send raw amounts with at least 1 nano.`);
- if (from.balanceBN.minus(rawAmount).lessThan(0)) return this.notificationService.sendError(`From account does not have enough XRB`);
+ if (from.balanceBN.minus(rawAmount).lessThan(0)) return this.notificationService.sendError(`From account does not have enough NANO`);
// Determine a proper raw amount to show in the UI, if a decimal was entered
this.amountRaw = this.rawAmount.mod(this.nano);
@@ -212,6 +212,13 @@ export class SendComponent implements OnInit {
this.confirmingTransaction = true;
try {
+ // New stuff to show status of each part of the transaction.
+ // console.log('Sending sub send command....');
+ // this.nanoBlock.subscribeSend(walletAccount, this.toAccountID, this.rawAmount, this.walletService.isLedgerWallet()).subscribe(value => {
+ // console.log('GOT VALUE!!! ', value)
+ // }, err => {
+ // console.log('GOT ERROR!!!: ', err);
+ // });
const newHash = await this.nanoBlock.generateSend(walletAccount, this.toAccountID, this.rawAmount, this.walletService.isLedgerWallet());
if (newHash) {
this.notificationService.sendSuccess(`Successfully sent ${this.amount} ${this.selectedAmount.shortName}!`);
diff --git a/src/app/services/address-book.service.ts b/src/app/services/address-book.service.ts
index e4ae737f..dd0cdf75 100644
--- a/src/app/services/address-book.service.ts
+++ b/src/app/services/address-book.service.ts
@@ -30,6 +30,24 @@ export class AddressBookService {
return this.addressBook;
}
+ patchXrbPrefixData() {
+ const addressBookStore = localStorage.getItem(this.storeKey);
+ if (!addressBookStore) return;
+
+ const addressBook = JSON.parse(addressBookStore);
+
+ const newAddressBook = addressBook.map(entry => {
+ if (entry.account.indexOf('xrb_') !== -1) {
+ entry.account = entry.account.replace('xrb_', 'nano_');
+ }
+ return entry;
+ });
+
+ localStorage.setItem(this.storeKey, JSON.stringify(newAddressBook));
+
+ return true;
+ }
+
async saveAddress(account, name) {
const existingName = this.addressBook.find(a => a.name.toLowerCase() === name.toLowerCase());
if (existingName) throw new Error(`Name already exists in the address book`);
diff --git a/src/app/services/app-settings.service.ts b/src/app/services/app-settings.service.ts
index 50d38639..2cd44fa7 100644
--- a/src/app/services/app-settings.service.ts
+++ b/src/app/services/app-settings.service.ts
@@ -18,6 +18,7 @@ interface AppSettings {
serverNode: string | null;
serverWS: string | null;
minimumReceive: string | null;
+ walletVersion: number | null;
}
@Injectable()
@@ -38,6 +39,7 @@ export class AppSettingsService {
serverNode: null,
serverWS: null,
minimumReceive: null,
+ walletVersion: 1
};
constructor() { }
@@ -91,6 +93,7 @@ export class AppSettingsService {
serverAPI: null,
serverWS: null,
minimumReceive: null,
+ walletVersion: 1,
};
}
diff --git a/src/app/services/nano-block.service.ts b/src/app/services/nano-block.service.ts
index ffc0a3c5..34d5bbb8 100644
--- a/src/app/services/nano-block.service.ts
+++ b/src/app/services/nano-block.service.ts
@@ -8,13 +8,14 @@ import {NotificationService} from "./notification.service";
import {AppSettingsService} from "./app-settings.service";
import {WalletService} from "./wallet.service";
import {LedgerService} from "./ledger.service";
+import {Observable} from "rxjs/Observable";
const nacl = window['nacl'];
const STATE_BLOCK_PREAMBLE = '0000000000000000000000000000000000000000000000000000000000000006';
@Injectable()
export class NanoBlockService {
- representativeAccount = 'xrb_3rw4un6ys57hrb39sy1qx8qy5wukst1iiponztrz9qiz6qqa55kxzx4491or'; // NanoVault Representative
+ representativeAccount = 'nano_3rw4un6ys57hrb39sy1qx8qy5wukst1iiponztrz9qiz6qqa55kxzx4491or'; // NanoVault Representative
constructor(
private api: ApiService,
@@ -83,6 +84,94 @@ export class NanoBlockService {
}
}
+ // This might be used in the future to send state changes on the blocks instead of normal true/false
+ // subscribeSend(walletAccount, toAccountID, rawAmount, ledger = false): Observable {
+ // const doSend = async (observable) => {
+ // console.log(`OBS: Promise resolve, running main send logic.`);
+ // const startTime = Date.now();
+ //
+ // console.log(`Observable: Creation event run`);
+ // observable.next({ step: 0, startTime: startTime });
+ //
+ //
+ // const fromAccount = await this.api.accountInfo(walletAccount.id);
+ // if (!fromAccount) throw new Error(`Unable to get account information for ${walletAccount.id}`);
+ //
+ // const remaining = new BigNumber(fromAccount.balance).minus(rawAmount);
+ // const remainingDecimal = remaining.toString(10);
+ // let remainingPadded = remaining.toString(16);
+ // while (remainingPadded.length < 32) remainingPadded = '0' + remainingPadded; // Left pad with 0's
+ //
+ // let blockData;
+ // const representative = fromAccount.representative || (this.settings.settings.defaultRepresentative || this.representativeAccount);
+ //
+ // observable.next({ step: 1, startTime: startTime, eventTime: ((Date.now() - startTime) / 1000).toFixed(3) });
+ //
+ // let signature = null;
+ // if (ledger) {
+ // const ledgerBlock = {
+ // previousBlock: fromAccount.frontier,
+ // representative: representative,
+ // balance: remainingDecimal,
+ // recipient: toAccountID,
+ // };
+ // try {
+ // this.sendLedgerNotification();
+ // await this.ledgerService.updateCache(walletAccount.index, fromAccount.frontier);
+ // const sig = await this.ledgerService.signBlock(walletAccount.index, ledgerBlock);
+ // this.clearLedgerNotification();
+ // signature = sig.signature;
+ //
+ // observable.next({ step: 2, startTime: startTime, eventTime: ((Date.now() - startTime) / 1000).toFixed(3) });
+ // } catch (err) {
+ // this.clearLedgerNotification();
+ // this.sendLedgerDeniedNotification(err);
+ // return;
+ // }
+ // } else {
+ // signature = this.signSendBlock(walletAccount, fromAccount, representative, remainingPadded, toAccountID);
+ // observable.next({ step: 2, startTime: startTime, eventTime: ((Date.now() - startTime) / 1000).toFixed(3) });
+ // }
+ //
+ // if (!this.workPool.workExists(fromAccount.frontier)) {
+ // this.notifications.sendInfo(`Generating Proof of Work...`);
+ // }
+ //
+ // blockData = {
+ // type: 'state',
+ // account: walletAccount.id,
+ // previous: fromAccount.frontier,
+ // representative: representative,
+ // balance: remainingDecimal,
+ // link: this.util.account.getAccountPublicKey(toAccountID),
+ // work: await this.workPool.getWork(fromAccount.frontier),
+ // signature: signature,
+ // };
+ //
+ // observable.next({ step: 3, startTime: startTime, eventTime: ((Date.now() - startTime) / 1000).toFixed(3) });
+ //
+ // const processResponse = await this.api.process(blockData);
+ // if (!processResponse || !processResponse.hash) throw new Error(processResponse.error || `Node returned an error`);
+ //
+ // observable.next({ step: 4, startTime: startTime, eventTime: ((Date.now() - startTime) / 1000).toFixed(3) });
+ //
+ // walletAccount.frontier = processResponse.hash;
+ // this.workPool.addWorkToCache(processResponse.hash); // Add new hash into the work pool
+ // this.workPool.removeFromCache(fromAccount.frontier);
+ //
+ // observable.complete();
+ // };
+ //
+ //
+ // console.log(`Creating observable... on send...`);
+ // // Create an observable that can be returned instantly.
+ // return new Observable(observable => {
+ //
+ // doSend(observable).then(val => console.log(val));
+ // });
+ //
+ // }
+
async generateSend(walletAccount, toAccountID, rawAmount, ledger = false) {
const fromAccount = await this.api.accountInfo(walletAccount.id);
if (!fromAccount) throw new Error(`Unable to get account information for ${walletAccount.id}`);
diff --git a/src/app/services/representative.service.ts b/src/app/services/representative.service.ts
index f6ecd34a..5550b927 100644
--- a/src/app/services/representative.service.ts
+++ b/src/app/services/representative.service.ts
@@ -242,6 +242,24 @@ export class RepresentativeService {
return list;
}
+ patchXrbPrefixData() {
+ const representativeStore = localStorage.getItem(this.storeKey);
+ if (!representativeStore) return;
+
+ const list = JSON.parse(representativeStore);
+
+ const newRepList = list.map(entry => {
+ if (entry.id.indexOf('xrb_') !== -1) {
+ entry.id = entry.id.replace('xrb_', 'nano_');
+ }
+ return entry;
+ });
+
+ localStorage.setItem(this.storeKey, JSON.stringify(newRepList));
+
+ return true;
+ }
+
getRepresentative(id): StoredRepresentative | undefined {
return this.representatives.find(rep => rep.id == id);
}
diff --git a/src/app/services/util.service.ts b/src/app/services/util.service.ts
index 74aba87f..acc2eb40 100644
--- a/src/app/services/util.service.ts
+++ b/src/app/services/util.service.ts
@@ -206,7 +206,7 @@ function generateAccountKeyPair(accountSecretKeyBytes) {
return nacl.sign.keyPair.fromSecretKey(accountSecretKeyBytes);
}
-function getPublicAccountID(accountPublicKeyBytes, prefix = 'xrb') {
+function getPublicAccountID(accountPublicKeyBytes, prefix = 'nano') {
const accountHex = util.uint8.toHex(accountPublicKeyBytes);
const keyBytes = util.uint4.toUint8(util.hex.toUint4(accountHex)); // For some reason here we go from u, to hex, to 4, to 8??
const checksum = util.uint5.toString(util.uint4.toUint5(util.uint8.toUint4(blake.blake2b(keyBytes, null, 5).reverse())));
diff --git a/src/app/services/wallet.service.ts b/src/app/services/wallet.service.ts
index de351749..14522980 100644
--- a/src/app/services/wallet.service.ts
+++ b/src/app/services/wallet.service.ts
@@ -106,6 +106,8 @@ export class WalletService {
// Find out if this is a send, with our account as a destination or not
const walletAccountIDs = this.wallet.accounts.map(a => a.id);
+ // If we have a minimum receive, once we know the account... add the amount to wallet pending? set pending to true
+
if (transaction.block.type == 'send' && walletAccountIDs.indexOf(transaction.block.destination) !== -1) {
// Perform an automatic receive
const walletAccount = this.wallet.accounts.find(a => a.id === transaction.block.destination);
@@ -118,12 +120,15 @@ export class WalletService {
await this.processPendingBlocks();
}
} else if (transaction.block.type == 'state') {
+ if (this.wallet.locked) {
+ this.notifications.sendWarning(`New incoming transaction - unlock the wallet to receive it!`, { length: 0, identifier: 'pending-locked' });
+ }
+
await this.processStateBlock(transaction);
}
// TODO: We don't really need to call to update balances, we should be able to balance on our own from here
-
- await this.reloadBalances();
+ await this.reloadBalances(false);
});
this.addressBook.addressBook$.subscribe(newAddressBook => {
@@ -132,21 +137,32 @@ export class WalletService {
}
async processStateBlock(transaction) {
+ // If we have a minimum receive, once we know the account... add the amount to wallet pending? set pending to true
if (transaction.is_send === 'true' && transaction.block.link_as_account) {
// This is an incoming send block, we want to perform a receive
const walletAccount = this.wallet.accounts.find(a => a.id === transaction.block.link_as_account);
if (!walletAccount) return; // Not for our wallet?
// Check for a min receive
+ const txAmount = new BigNumber(transaction.amount);
+
+ if (this.wallet.pending.lte(0)) {
+ this.wallet.pending = this.wallet.pending.plus(txAmount);
+ this.wallet.pendingRaw = this.wallet.pendingRaw.plus(txAmount.mod(this.nano));
+ this.wallet.pendingFiat += this.util.nano.rawToMnano(txAmount).times(this.price.price.lastPrice).toNumber();
+ this.wallet.hasPending = true;
+ }
+
if (this.appSettings.settings.minimumReceive) {
const minAmount = this.util.nano.mnanoToRaw(this.appSettings.settings.minimumReceive);
- if (new BigNumber(transaction.amount).gt(minAmount)) {
- this.addPendingBlock(walletAccount.id, transaction.hash, new BigNumber(0));
+
+ if (txAmount.gt(minAmount)) {
+ this.addPendingBlock(walletAccount.id, transaction.hash, txAmount);
} else {
console.log(`Found new pending block that was below minimum receive amount: `, transaction.amount, this.appSettings.settings.minimumReceive);
}
} else {
- this.addPendingBlock(walletAccount.id, transaction.hash, new BigNumber(0));
+ this.addPendingBlock(walletAccount.id, transaction.hash, txAmount);
}
await this.processPendingBlocks();
@@ -167,6 +183,30 @@ export class WalletService {
return this.wallet.accounts.find(a => a.id == accountID);
}
+
+ async patchOldSavedData() {
+ // Look for saved accounts using an xrb_ prefix
+ const walletData = localStorage.getItem(this.storeKey);
+ if (!walletData) return true;
+
+ const walletJson = JSON.parse(walletData);
+
+ if (walletJson.accounts) {
+ const newAccounts = walletJson.accounts.map(account => {
+ if (account.id.indexOf('xrb_') !== -1) {
+ account.id = account.id.replace('xrb_', 'nano_');
+ }
+ return account;
+ });
+
+ walletJson.accounts = newAccounts;
+ }
+
+ localStorage.setItem(this.storeKey, JSON.stringify(walletJson));
+
+ return true;
+ }
+
async loadStoredWallet() {
this.resetWallet();
@@ -557,6 +597,11 @@ export class WalletService {
let hasPending: boolean = false;
+ // If this is just a normal reload.... do not use the minimum receive setting?
+ if (!reloadPending && walletPending.gt(0)) {
+ hasPending = true; // Temporary override? New incoming transaction on half reload? skip?
+ }
+
// Check if there is a pending balance at all
if (walletPending.gt(0)) {
// If we have a minimum receive amount, check accounts for actual receivable transactions
@@ -566,10 +611,13 @@ export class WalletService {
if (pending && pending.blocks) {
for (let block in pending.blocks) {
- if (!pending.blocks.hasOwnProperty(block)) continue;
+ if (!pending.blocks.hasOwnProperty(block)) {
+ continue;
+ }
if (pending.blocks[block]) {
hasPending = true;
} else {
+ console.log('Pendling loop - no match, skipping? ', block);
}
}
}
@@ -725,7 +773,6 @@ export class WalletService {
}
}
- // Now, only if we have results, do a unique on the account names, and run account info on all of them?
if (this.pendingBlocks.length) {
this.processPendingBlocks();
}