Skip to content

Commit

Permalink
Fix sending tx (#516)
Browse files Browse the repository at this point in the history
* Fixed sending tx

* Fixed sending tx

* Fixed sending tx

* Fixed sending tx

* Fixed sending tx

Co-authored-by: Ilya <[email protected]>
  • Loading branch information
IlyaVi and Ilya authored May 21, 2020
1 parent bf5508e commit f7b45eb
Show file tree
Hide file tree
Showing 20 changed files with 628 additions and 521 deletions.
4 changes: 2 additions & 2 deletions app/components/node/Carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { Component } from 'react';
import styled from 'styled-components';
import { chevronLeftBlack, chevronRightBlack, chevronLeftGray, chevronRightGray } from '/assets/images';
import { smColors } from '/vars';
import { formatNumber } from '/infra/utils';
import { formatBytes } from '/infra/utils';

const SLIDE_WIDTH = 170;
const SLIDE_MARGIN = 15;
Expand Down Expand Up @@ -160,7 +160,7 @@ class Carousel extends Component<Props, State> {
<Text>
FREE SPACE...
<br />
{formatNumber(element.availableDiskSpace)} GB
{formatBytes(element.availableDiskSpace)}
</Text>
</TextWrapper>
</SlideUpperPart>
Expand Down
4 changes: 3 additions & 1 deletion app/components/transactions/TransactionRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { updateTransaction } from '/redux/wallet/actions';
import { chevronLeftBlack, chevronRightBlack, addContact } from '/assets/images';
import styled from 'styled-components';
import { Button } from '/basicComponents';
import { getAbbreviatedText, formatTxId, getFormattedTimestamp, getAddress, formatSmidge } from '/infra/utils';
import { getAbbreviatedText, getFormattedTimestamp, getAddress, formatSmidge } from '/infra/utils';
import { smColors } from '/vars';
import TX_STATUSES from '/vars/enums';
import type { Tx, Action } from '/types';
Expand Down Expand Up @@ -141,6 +141,8 @@ const TextArea = styled.textarea`
margin-bottom: 10px;
`;

const formatTxId = (id) => id && `0x${id.substring(0, 6)}`;

type Props = {
updateTransaction: Action,
tx: Tx,
Expand Down
2 changes: 1 addition & 1 deletion app/components/wallet/TxConfirmation.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class TxConfirmation extends PureComponent<Props> {
<ComplexButtonText>EDIT TRANSACTION</ComplexButtonText>
</ComplexButton>
<Link onClick={this.navigateToGuide} text="SEND SMH GUIDE" />
<Button onClick={doneAction} text="SEND" isDisabled={!status.synced} />
<Button onClick={doneAction} text="SEND" isDisabled={!status.synced} style={{ marginLeft: 'auto' }} />
{!status?.synced && <NotSyncedExplanation>Please wait until your app is synced with the mesh</NotSyncedExplanation>}
</Footer>
</Wrapper>
Expand Down
1 change: 0 additions & 1 deletion app/infra/cryptoService/index.js

This file was deleted.

13 changes: 6 additions & 7 deletions app/infra/eventsService/eventsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import { ipcRenderer } from 'electron';
import { ipcConsts } from '/vars';

class EventsService {
static createWallet = ({ timestamp, dataToEncrypt, password }: { timestamp: string, dataToEncrypt: Object, password: string }) =>
ipcRenderer.invoke(ipcConsts.CREATE_WALLET_FILE, { timestamp, dataToEncrypt, password });
static createWallet = ({ password, existingMnemonic }: { password: string, existingMnemonic: string }) =>
ipcRenderer.invoke(ipcConsts.CREATE_WALLET_FILE, { password, existingMnemonic });

static readWalletFiles = () => ipcRenderer.invoke(ipcConsts.READ_WALLET_FILES);

static unlockWallet = ({ path, password }: { path: string, password: string }) => ipcRenderer.invoke(ipcConsts.UNLOCK_WALLET_FILE, { path, password });

static updateWallet = ({ fileName, password, data }: { fileName: string, password: string, data: Object }) =>
ipcRenderer.invoke(ipcConsts.UPDATE_WALLET_FILE, { fileName, password, data });
ipcRenderer.send(ipcConsts.UPDATE_WALLET_FILE, { fileName, password, data });

static createNewAccount = ({ fileName, password }: { fileName: string, password: string }) => ipcRenderer.invoke(ipcConsts.CREATE_NEW_ACCOUNT, { fileName, password });

static copyFile = ({ filePath, copyToDocuments }: { filePath: string, copyToDocuments?: boolean }) => ipcRenderer.invoke(ipcConsts.COPY_FILE, { filePath, copyToDocuments });

Expand Down Expand Up @@ -53,10 +55,7 @@ class EventsService {

static getBalance = ({ address }: { address: string }) => ipcRenderer.invoke(ipcConsts.GET_BALANCE, { address });

static getNonce = ({ address }: { address: string }) => ipcRenderer.invoke(ipcConsts.GET_NONCE, { address });

static sendTx = ({ tx, accountIndex, txToAdd }: { tx: Uint8Array, accountIndex: number, txToAdd: Object }) =>
ipcRenderer.invoke(ipcConsts.SEND_TX, { tx, accountIndex, txToAdd });
static sendTx = ({ fullTx, accountIndex }: { fullTx: Object, accountIndex: number }) => ipcRenderer.invoke(ipcConsts.SEND_TX, { fullTx, accountIndex });

static updateTransaction = ({ newData, accountIndex, txId }: { newData: string, accountIndex: number, txId?: string }) =>
ipcRenderer.invoke(ipcConsts.UPDATE_TX, { newData, accountIndex, txId });
Expand Down
136 changes: 15 additions & 121 deletions app/infra/utils.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
const fromHexString = (hexString) => {
const bytes = [];
for (let i = 0; i < hexString.length; i += 2) {
bytes.push(parseInt(hexString.slice(i, i + 2), 16));
}
return Uint8Array.from(bytes);
};

const toHexString = (bytes) => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');

const createError = (message, func) => ({
message,
retryFunction: func
Expand All @@ -16,9 +6,21 @@ const createError = (message, func) => ({
const getAbbreviatedText = (address: string, addPrefix: boolean = true, tailSize: number = 4) =>
`${addPrefix && address.indexOf('0x') === -1 ? '0x' : ''}${address.substring(0, tailSize)}...${address.substring(address.length - tailSize, address.length)}`;

const formatTxId = (id) => id && `0x${id.substring(0, 6)}`;
const getFormattedTimestamp = (timestamp: string) => {
if (timestamp) {
const options = { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' };
const dateObj = new Date(timestamp);
return dateObj.toLocaleDateString('en-US', options).replace(',', '');
}
return null;
};

const formatNumber = (num) => num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
const getAddress = (key: string) => (key ? key.substring(24) : null);

const formatBytes = (bytes) => {
if (bytes === 0) return 0;
return parseFloat((bytes / 1073741824).toFixed(4));
};

// Internal helper - returns the value and the unit of a smidge coin amount.
// Used to format smidge strings
Expand Down Expand Up @@ -55,112 +57,4 @@ const formatSmidge = (amount: number, separateResult) => {
return separateResult ? { value: res.value, unit: res.unit } : `${res.value} ${res.unit}`;
};

const testGetValueAndUnit = () => {
let res = getValueAndUnit(0);
if (res.unit !== 'SMH' || res.value !== 0) {
console.error(`test failed. expected 0 SMH: ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(2.5 * 10 ** 12);
if (res.unit !== 'SMH' || res.value !== 2.5) {
console.error(`test failed. expected 2.5 SMH: ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 12);
if (res.unit !== 'SMH' || res.value !== 1) {
console.error(`test failed. expected 1 SMH${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 11);
if (res.unit !== 'SMH' || res.value !== 0.1) {
console.error(`test failed. expected 0.1 SMH${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 10);
if (res.unit !== 'SMH' || res.value !== 0.01) {
console.error(`test failed. expected 0.01 SMH${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 9);
if (res.unit !== 'SMH' || res.value !== 0.001) {
console.error(`test failed. expected 0.001 SMH. ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 8);
if (res.unit !== 'GSMD' || res.value !== 0.1) {
console.error(`test failed. expected 0.1 GSMD. ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 7);
if (res.unit !== 'GSMD' || res.value !== 0.01) {
console.error(`test failed. expected 0.01 GSMD ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 6);
if (res.unit !== 'GSMD' || res.value !== 0.001) {
console.error(`test failed. expected 0.001 GSMD ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 5);
if (res.unit !== 'MSMD' || res.value !== 0.1) {
console.error(`test failed. expected 0.1 MSMD ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 4);
if (res.unit !== 'MSMD' || res.value !== 0.01) {
console.error(`test failed. expected 0.01 MSMD ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 3);
if (res.unit !== 'SMD' || res.value !== 1000) {
console.error(`test failed. expected 1000 SMD ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10 ** 2);
if (res.unit !== 'SMD' || res.value !== 100) {
console.error(`test failed. expected 100 SMD ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(10);
if (res.unit !== 'SMD' || res.value !== 10) {
console.error(`test failed. expected 10 SMD ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console

res = getValueAndUnit(1);
if (res.unit !== 'SMD' || res.value !== 1) {
console.error(`test failed. expected 1 SMD ${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
}
console.log(`${res.value.toString()} ${res.unit}`); // eslint-disable-line no-console
};

const getFormattedTimestamp = (timestamp: string) => {
if (timestamp) {
const options = { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' };
const dateObj = new Date(timestamp);
return dateObj.toLocaleDateString('en-US', options).replace(',', '');
}
return null;
};

const getAddress = (key: string) => (key ? key.substring(24) : null);

const formatBytes = (bytes) => {
if (bytes === 0) return 0;
return parseFloat((bytes / 1073741824).toFixed(4));
};

export { testGetValueAndUnit, formatSmidge, fromHexString, toHexString, createError, getAbbreviatedText, formatNumber, getFormattedTimestamp, getAddress, formatTxId, formatBytes };
export { formatSmidge, createError, getAbbreviatedText, getFormattedTimestamp, getAddress, formatBytes };
2 changes: 0 additions & 2 deletions app/redux/auth/actions.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// @flow
import { Action } from '/types';
import { cryptoService } from '/infra/cryptoService';

export const LOGOUT: string = 'LOGOUT';

export const logout = (): Action => {
cryptoService.stopAndCleanUp();
return { type: LOGOUT };
};
64 changes: 20 additions & 44 deletions app/redux/wallet/actions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// @flow
import { eventsService } from '/infra/eventsService';
import { cryptoService } from '/infra/cryptoService';
import { createError, getAddress } from '/infra/utils';
import { Action, Dispatch, GetState, WalletMeta, Account, AccountTxs, Contact } from '/types';
import TX_STATUSES from '/vars/enums';
Expand All @@ -20,14 +19,6 @@ export const SET_BACKUP_TIME: string = 'SET_BACKUP_TIME';

export const SET_UPDATE_DOWNLOADING: string = 'IS_UPDATE_DOWNLOADING';

const getNewAccountFromTemplate = ({ accountNumber, timestamp, publicKey, secretKey }: { accountNumber: number, timestamp: string, publicKey: string, secretKey: string }) => ({
displayName: accountNumber > 0 ? `Account ${accountNumber}` : 'Main Account',
created: timestamp,
path: `0/0/${accountNumber}`,
publicKey,
secretKey
});

export const setWalletMeta = ({ meta }: { meta: WalletMeta }): Action => ({ type: SET_WALLET_META, payload: { meta } });

export const setAccounts = ({ accounts }: { accounts: Account[] }): Action => ({ type: SET_ACCOUNTS, payload: { accounts } });
Expand All @@ -40,23 +31,15 @@ export const setTransactions = ({ transactions }: { transactions: AccountTxs }):

export const setContacts = ({ contacts }: { contacts: Contact[] }): Action => ({ type: SET_CONTACTS, payload: { contacts } });

export const createNewWallet = ({ mnemonic, password }: { mnemonic?: string, password: string }): Action => async (dispatch: Dispatch): Dispatch => {
const timestamp = new Date().toISOString().replace(/:/g, '-');
const resolvedMnemonic = mnemonic || cryptoService.generateMnemonic();
const { publicKey, secretKey } = cryptoService.generateKeyPair({ mnemonic: resolvedMnemonic });
const dataToEncrypt = {
mnemonic: resolvedMnemonic,
accounts: [getNewAccountFromTemplate({ accountNumber: 0, timestamp, publicKey, secretKey })]
};
const { error, meta } = await eventsService.createWallet({ timestamp, dataToEncrypt, password });
export const createNewWallet = ({ existingMnemonic, password }: { existingMnemonic?: string, password: string }): Action => async (dispatch: Dispatch): Dispatch => {
const { error, accounts, mnemonic, meta } = await eventsService.createWallet({ password, existingMnemonic });
if (error) {
console.log(error); // eslint-disable-line no-console
throw createError('Error creating new wallet!', () => dispatch(createNewWallet({ mnemonic, password })));
throw createError('Error creating new wallet!', () => dispatch(createNewWallet({ existingMnemonic, password })));
} else {
dispatch(setWalletMeta({ meta }));
dispatch(setAccounts({ accounts: dataToEncrypt.accounts }));
dispatch(setMnemonic({ mnemonic: resolvedMnemonic }));
localStorage.setItem('accountNumber', '1');
dispatch(setAccounts({ accounts }));
dispatch(setMnemonic({ mnemonic }));
dispatch(readWalletFiles());
}
};
Expand Down Expand Up @@ -95,15 +78,14 @@ export const updateWalletName = ({ displayName }: { displayName: string }): Acti
};

export const createNewAccount = ({ password }: { password: string }): Action => async (dispatch: Dispatch, getState: GetState): Dispatch => {
const { walletFiles, mnemonic, accounts } = getState().wallet;
const { publicKey, secretKey } = cryptoService.deriveNewKeyPair({ mnemonic, index: accounts.length });
const timestamp = new Date().toISOString().replace(/:/, '-');
const accountNumber = JSON.parse(localStorage.getItem('accountNumber') || '1');
const newAccount = getNewAccountFromTemplate({ accountNumber, timestamp, publicKey, secretKey });
const updatedAccounts = [...accounts, newAccount];
await eventsService.updateWalletFile({ fileName: walletFiles[0], password, data: { mnemonic, accounts: updatedAccounts } });
dispatch(setAccounts({ accounts: updatedAccounts }));
localStorage.setItem('accountNumber', `${accountNumber + 1}`);
const { walletFiles, accounts } = getState().wallet;
const { error, newAccount } = await eventsService.createNewAccount({ fileName: walletFiles[0], password });
if (error) {
console.log(error); // eslint-disable-line no-console
throw createError('Failed to create new account', () => dispatch(createNewAccount({ password })));
} else {
dispatch(setAccounts({ accounts: [...accounts, newAccount] }));
}
};

export const updateAccountName = ({ accountIndex, name, password }: { accountIndex: number, name: string, password: string }): Action => async (
Expand Down Expand Up @@ -157,32 +139,26 @@ export const getBalance = (): Action => async (dispatch: Dispatch, getState: Get
}
};

export const sendTransaction = ({ recipient, amount, fee, note }: { recipient: string, amount: number, fee: number, note: string }): Action => async (
export const sendTransaction = ({ receiver, amount, fee, note }: { receiver: string, amount: number, fee: number, note: string }): Action => async (
dispatch: Dispatch,
getState: GetState
): Dispatch => {
const { accounts, currentAccountIndex } = getState().wallet;
const { nonce } = await eventsService.getNonce({ address: accounts[currentAccountIndex].publicKey });
const { tx } = await cryptoService.signTransaction({
accountNonce: nonce,
recipient,
price: fee,
amount,
secretKey: accounts[currentAccountIndex].secretKey
});
const txToAdd = {
const fullTx = {
sender: getAddress(accounts[currentAccountIndex].publicKey),
receiver: recipient,
receiver,
amount,
fee,
status: TX_STATUSES.PENDING,
timestamp: new Date().getTime(),
note
};
const { error, transactions, id } = await eventsService.sendTx({ tx, accountIndex: currentAccountIndex, txToAdd });
const { error, transactions, id } = await eventsService.sendTx({ fullTx, accountIndex: currentAccountIndex });
if (error) {
console.log(error); // eslint-disable-line no-console
throw createError('Error sending transaction!', () => dispatch(sendTransaction({ recipient, amount, fee, note })));
throw createError('Error sending transaction!', () => {
dispatch(sendTransaction({ receiver, amount, fee, note }));
});
} else {
dispatch(setTransactions({ transactions }));
return id;
Expand Down
2 changes: 1 addition & 1 deletion app/screens/auth/CreateWallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class CreateWallet extends Component<Props, State> {
this.setState({ isLoaderVisible: true });
try {
await setTimeout(async () => {
createNewWallet({ mnemonic: location?.state?.mnemonic, password });
createNewWallet({ existingMnemonic: location?.state?.mnemonic, password });
this.setState({ isLoaderVisible: false, subMode: 2 });
}, 500);
} catch (err) {
Expand Down
11 changes: 9 additions & 2 deletions app/screens/auth/WordsRestore.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// @flow
import * as bip39 from 'bip39';
import { shell } from 'electron';
import React, { Component } from 'react';
import styled from 'styled-components';
import { BackButton } from '/components/common';
import { WrapperWith2SideBars, Input, Button, Link, ErrorPopup, SmallHorizontalPanel } from '/basicComponents';
import { cryptoService } from '/infra/cryptoService';
import { smColors } from '/vars';
import type { RouterHistory } from 'react-router-dom';

Expand Down Expand Up @@ -120,11 +120,18 @@ class WordsRestore extends Component<Props, State> {
return words.every((word) => !!word && word.trim().length > 0) || hasError;
};

validateMnemonic = ({ mnemonic }: { mnemonic: string }) => {
if (!mnemonic || !mnemonic.length) {
return false;
}
return bip39.validateMnemonic(mnemonic);
};

restoreWith12Words = () => {
const { history } = this.props;
const { words } = this.state;
const mnemonic = Object.values(words).join(' ');
if (cryptoService.validateMnemonic({ mnemonic })) {
if (this.validateMnemonic({ mnemonic })) {
history.push('/auth/create', { mnemonic });
} else {
this.setState({ hasError: true });
Expand Down
Loading

0 comments on commit f7b45eb

Please sign in to comment.