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  }