mirror of https://github.com/trezor/trezor-wallet
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.
237 lines
7.7 KiB
237 lines
7.7 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.features.device_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.features.device_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.features.device_id === device.features.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;
|
|
|
|
const index: number = parseInt(locationState.account, 10);
|
|
|
|
return state.accounts.find(
|
|
a =>
|
|
a.deviceState === device.state &&
|
|
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.rejected) return value;
|
|
return Math.max(value, tx.sequence + 1);
|
|
}, 0);
|
|
|
|
export const getPendingAmount = (
|
|
pending: Array<Transaction>,
|
|
currency: string,
|
|
token: boolean = false
|
|
): BigNumber =>
|
|
pending.reduce((value: BigNumber, tx: Transaction): BigNumber => {
|
|
if (tx.type !== 'send') 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.shortcut === currency);
|
|
const tokensValue: BigNumber = allTokens.reduce(
|
|
(tv, t) => new BigNumber(value).plus(t.value),
|
|
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;
|
|
};
|