sagas.ts
1 import { SagaIterator } from 'redux-saga'; 2 import { all, apply, fork, put, select, takeEvery, takeLatest } from 'redux-saga/effects'; 3 import { publicToAddress } from 'ethereumjs-util'; 4 import HDKey from 'hdkey'; 5 6 import { translateRaw } from 'translations'; 7 import { INode } from 'libs/nodes/INode'; 8 import { TokenValue } from 'libs/units'; 9 import { Token } from 'types/network'; 10 import * as derivedSelectors from 'features/selectors'; 11 import { getChecksumAddressFn } from 'features/config'; 12 import * as configNodesSelectors from 'features/config/nodes/selectors'; 13 import { notificationsActions } from 'features/notifications'; 14 import * as types from './types'; 15 import * as actions from './actions'; 16 import * as selectors from './selectors'; 17 18 export function* getDeterministicWalletsSaga( 19 action: types.GetDeterministicWalletsAction 20 ): SagaIterator { 21 const { seed, dPath, publicKey, chainCode, limit, offset } = action.payload; 22 let pathBase; 23 let hdk; 24 25 // if seed present, treat as mnemonic 26 // if pubKey & chainCode present, treat as HW wallet 27 28 if (seed) { 29 hdk = HDKey.fromMasterSeed(new Buffer(seed, 'hex')); 30 pathBase = dPath; 31 } else if (publicKey && chainCode) { 32 hdk = new HDKey(); 33 hdk.publicKey = new Buffer(publicKey, 'hex'); 34 hdk.chainCode = new Buffer(chainCode, 'hex'); 35 pathBase = 'm'; 36 } else { 37 return; 38 } 39 const wallets: types.DeterministicWalletData[] = []; 40 const toChecksumAddress: ReturnType<typeof getChecksumAddressFn> = yield select( 41 getChecksumAddressFn 42 ); 43 for (let i = 0; i < limit; i++) { 44 const index = i + offset; 45 const dkey = hdk.derive(`${pathBase}/${index}`); 46 const address = publicToAddress(dkey.publicKey, true).toString('hex'); 47 wallets.push({ 48 index, 49 address: toChecksumAddress(address), 50 tokenValues: {} 51 }); 52 } 53 54 yield put(actions.setDeterministicWallets(wallets)); 55 yield fork(updateWalletValues); 56 yield fork(updateWalletTokenValues); 57 } 58 59 // Grab each wallet's main network token, and update it with it 60 export function* updateWalletValues(): SagaIterator { 61 const node: INode = yield select(configNodesSelectors.getNodeLib); 62 const wallets: types.DeterministicWalletData[] = yield select(selectors.getWallets); 63 64 try { 65 const calls = wallets.map(w => apply(node, node.getBalance, [w.address])); 66 const balances = yield all(calls); 67 68 for (let i = 0; i < wallets.length; i++) { 69 yield put( 70 actions.updateDeterministicWallet({ 71 ...wallets[i], 72 value: balances[i] 73 }) 74 ); 75 } 76 } catch (err) { 77 console.log(err); 78 yield put(notificationsActions.showNotification('danger', translateRaw('ERROR_32'))); 79 } 80 } 81 82 // Grab the current desired token, and update the wallet with it 83 export function* updateWalletTokenValues(): SagaIterator { 84 const desiredToken: string = yield select(selectors.getDesiredToken); 85 if (!desiredToken) { 86 return; 87 } 88 89 const tokens: Token[] = yield select(derivedSelectors.getTokens); 90 const token = tokens.find(t => t.symbol === desiredToken); 91 if (!token) { 92 return; 93 } 94 95 const node: INode = yield select(configNodesSelectors.getNodeLib); 96 const wallets: types.DeterministicWalletData[] = yield select(selectors.getWallets); 97 98 try { 99 const calls = wallets.map(w => { 100 return apply(node, node.getTokenBalance, [w.address, token]); 101 }); 102 const tokenBalances: { balance: TokenValue; error: string | null }[] = yield all(calls); 103 104 for (let i = 0; i < wallets.length; i++) { 105 if (!tokenBalances[i].error) { 106 yield put( 107 actions.updateDeterministicWallet({ 108 ...wallets[i], 109 tokenValues: { 110 ...wallets[i].tokenValues, 111 [desiredToken]: { 112 value: tokenBalances[i].balance, 113 decimal: token.decimal 114 } 115 } 116 }) 117 ); 118 } 119 } 120 } catch (err) { 121 console.log(err); 122 yield put(notificationsActions.showNotification('danger', translateRaw('ERROR_32'))); 123 } 124 } 125 126 export function* deterministicWalletsSaga(): SagaIterator { 127 yield takeLatest('DETERMINISTIC_WALLETS_GET_WALLETS', getDeterministicWalletsSaga); 128 yield takeEvery('DETERMINISTIC_WALLETS_UPDATE_WALLET', updateWalletTokenValues); 129 }