You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-wallet/src/reducers/utils/index.js

236 lines
7.9 KiB

/* @flow */
import BigNumber from 'bignumber.js';
import type {
State,
Device,
TrezorDevice,
Account,
Network,
Discovery,
Token,
Transaction,
Web3Instance,
} from 'flowtype';
export const getSelectedDevice = (state: State): ?TrezorDevice => {
const locationState = state.router.location.state;
if (!locationState.device) return undefined;
const instance: ?number = locationState.deviceInstance
? parseInt(locationState.deviceInstance, 10)
: undefined;
return state.devices.find(d => {
if (!d.features && d.path === locationState.device) {
return true;
}
if (d.mode === 'bootloader' && d.path === locationState.device) {
return true;
}
if (d.features && d.id === locationState.device && d.instance === instance) {
return true;
}
return false;
});
};
// find device by id and state
export const findDevice = (
devices: Array<TrezorDevice>,
deviceId: string,
deviceState: string /*, instance: ?number*/
): ?TrezorDevice =>
devices.find(d => {
// TODO: && (instance && d.instance === instance)
if (d.features && d.id === deviceId && d.state === deviceState) {
return true;
}
return false;
});
// get next instance number
export const getDuplicateInstanceNumber = (
devices: Array<TrezorDevice>,
device: Device | TrezorDevice
): number => {
// find device(s) with the same features.device_id
// and sort them by instance number
const affectedDevices: Array<TrezorDevice> = devices
.filter(d => d.features && device.features && d.id === device.id)
.sort((a, b) => {
if (!a.instance) {
return -1;
}
return !b.instance || a.instance > b.instance ? 1 : -1;
});
// calculate new instance number
const instance: number = affectedDevices.reduce(
(inst, dev) => (dev.instance ? dev.instance + 1 : inst + 1),
0
);
return instance;
};
export const getSelectedAccount = (state: State): ?Account => {
const device = state.wallet.selectedDevice;
const locationState = state.router.location.state;
if (!device || !locationState.network || !locationState.account) return null;
// imported account index has 'i' prefix
const isImported = /^i\d+$/i.test(locationState.account);
const index: number = isImported
? parseInt(locationState.account.substr(1), 10)
: parseInt(locationState.account, 10);
return state.accounts.find(
a =>
a.imported === isImported &&
(a.deviceState === device.state || (a.imported && a.deviceID === device.id)) &&
a.index === index &&
a.network === locationState.network
);
};
export const getSelectedNetwork = (state: State): ?Network => {
const device = state.wallet.selectedDevice;
const { networks } = state.localStorage.config;
const locationState = state.router.location.state;
if (!device || !locationState.network) return null;
return networks.find(c => c.shortcut === locationState.network);
};
export const getDiscoveryProcess = (state: State): ?Discovery => {
const device = state.wallet.selectedDevice;
const locationState = state.router.location.state;
if (!device || !locationState.network) return null;
return state.discovery.find(
d => d.deviceState === device.state && d.network === locationState.network
);
};
export const getAccountPendingTx = (
pending: Array<Transaction>,
account: ?Account
): Array<Transaction> => {
const a = account;
if (!a) return [];
return pending.filter(p => p.network === a.network && p.descriptor === a.descriptor);
};
export const getPendingSequence = (pending: Array<Transaction>): number =>
pending.reduce((value: number, tx: Transaction): number => {
if (!tx.ethereumSpecific || tx.rejected) return value;
return Math.max(value, tx.ethereumSpecific.nonce + 1);
}, 0);
export const getPendingAmount = (
pending: Array<Transaction>,
currency: string,
token: boolean = false
): BigNumber =>
pending.reduce((value: BigNumber, tx: Transaction): BigNumber => {
if (tx.type !== 'sent') return value;
if (!token) {
// regular transactions
// add fees from token txs and amount from regular txs
return new BigNumber(value).plus(tx.tokens ? tx.fee : tx.total);
}
if (tx.tokens) {
// token transactions
const allTokens = tx.tokens.filter(t => t.symbol === currency);
const tokensValue: BigNumber = allTokens.reduce(
(tv, t) => new BigNumber(value).plus(t.amount),
new BigNumber('0')
);
return new BigNumber(value).plus(tokensValue);
}
// default
return value;
}, new BigNumber('0'));
export const findToken = (
state: Array<Token>,
address: string,
symbol: string,
deviceState: string
): ?Token =>
state.find(
t => t.ethAddress === address && t.symbol === symbol && t.deviceState === deviceState
);
export const getAccountTokens = (tokens: Array<Token>, account: ?Account): Array<Token> => {
const a = account;
if (!a) return [];
return tokens.filter(
t =>
t.ethAddress === a.descriptor &&
t.network === a.network &&
t.deviceState === a.deviceState
);
};
export const getWeb3 = (state: State): ?Web3Instance => {
const locationState = state.router.location.state;
if (!locationState.network) return null;
return state.web3.find(w3 => w3.network === locationState.network);
};
export const observeChanges = (
prev: ?any,
current: ?any,
filter?: { [k: string]: Array<string> }
): boolean => {
// 1. both objects are the same (solves simple types like string, boolean and number)
if (prev === current) return false;
// 2. one of the objects is null/undefined
if (!prev || !current) return true;
const prevType = Object.prototype.toString.call(prev);
const currentType = Object.prototype.toString.call(current);
// 3. one of the objects has different type then other
if (prevType !== currentType) return true;
if (currentType === '[object Array]') {
// 4. Array length is different
if (prev.length !== current.length) return true;
// observe array recursive
for (let i = 0; i < current.length; i++) {
if (observeChanges(prev[i], current[i], filter)) return true;
}
} else if (currentType === '[object Object]') {
const prevKeys = Object.keys(prev);
const currentKeys = Object.keys(current);
// 5. simple validation of keys length
if (prevKeys.length !== currentKeys.length) return true;
// 6. "prev" has keys which "current" doesn't have
const prevDifference = prevKeys.find(k => currentKeys.indexOf(k) < 0);
if (prevDifference) return true;
// 8. observe every key recursive
for (let i = 0; i < currentKeys.length; i++) {
const key = currentKeys[i];
if (filter && filter.hasOwnProperty(key) && prev[key] && current[key]) {
const prevFiltered = {};
const currentFiltered = {};
for (let i2 = 0; i2 < filter[key].length; i2++) {
const field = filter[key][i2];
prevFiltered[field] = prev[key][field];
currentFiltered[field] = current[key][field];
}
if (observeChanges(prevFiltered, currentFiltered)) return true;
} else if (observeChanges(prev[key], current[key])) {
return true;
}
}
} else if (prev !== current) {
// solve simple types like string, boolean and number
return true;
}
return false;
};