sagas.ts
1 import { SagaIterator } from 'redux-saga'; 2 import { put, take, all, apply, takeEvery, call, select } from 'redux-saga/effects'; 3 4 import { translateRaw } from 'translations'; 5 import { verifySignedMessage } from 'libs/signing'; 6 import { IFullWallet } from 'libs/wallet'; 7 import { padLeftEven } from 'libs/values'; 8 import Web3Node from 'libs/nodes/web3'; 9 import * as configNodesSelectors from 'features/config/nodes/selectors'; 10 import { notificationsActions } from 'features/notifications'; 11 import { paritySignerTypes, paritySignerActions } from 'features/paritySigner'; 12 import { walletSelectors } from 'features/wallet'; 13 import * as types from './types'; 14 import * as actions from './actions'; 15 16 export function* signingWrapper( 17 handler: (wallet: IFullWallet, message: string) => SagaIterator, 18 action: types.SignMessageRequestedAction 19 ): SagaIterator { 20 const payloadMessage = action.payload; 21 const wallet = yield select(walletSelectors.getWalletInst); 22 23 try { 24 yield call(handler, wallet, payloadMessage); 25 } catch (err) { 26 yield put( 27 notificationsActions.showNotification( 28 'danger', 29 translateRaw('SIGN_MSG_FAIL', { $err: err.message }), 30 5000 31 ) 32 ); 33 yield put(actions.signMessageFailed()); 34 } 35 } 36 37 export function messageToData(messageToTransform: string): string { 38 return ( 39 '0x' + 40 Array.from(Buffer.from(messageToTransform, 'utf8')) 41 .map(n => padLeftEven(n.toString(16))) 42 .join('') 43 ); 44 } 45 46 function* signLocalMessage(wallet: IFullWallet, msg: string): SagaIterator { 47 const address = yield apply(wallet, wallet.getAddressString); 48 const nodeLib: Web3Node = yield select(configNodesSelectors.getNodeLib); 49 const sig: string = yield apply(wallet, wallet.signMessage, [msg, nodeLib]); 50 51 yield put( 52 actions.signLocalMessageSucceeded({ 53 address, 54 msg, 55 sig, 56 version: '2' 57 }) 58 ); 59 } 60 61 function* signParitySignerMessage(wallet: IFullWallet, msg: string): SagaIterator { 62 const address = yield apply(wallet, wallet.getAddressString); 63 const data = yield call(messageToData, msg); 64 65 yield put(paritySignerActions.requestMessageSignature(address, data)); 66 67 const { payload: sig }: paritySignerTypes.FinalizeSignatureAction = yield take( 68 paritySignerTypes.ParitySignerActions.FINALIZE_SIGNATURE 69 ); 70 71 if (!sig) { 72 throw new Error(translateRaw('ERROR_38')); 73 } 74 75 yield put( 76 actions.signLocalMessageSucceeded({ 77 address, 78 msg, 79 sig, 80 version: '2' 81 }) 82 ); 83 } 84 85 function* handleMessageRequest(action: types.SignMessageRequestedAction): SagaIterator { 86 const walletType: walletSelectors.IWalletType = yield select(walletSelectors.getWalletType); 87 88 const signingHandler = walletType.isParitySignerWallet 89 ? signParitySignerMessage 90 : signLocalMessage; 91 92 return yield call(signingWrapper, signingHandler, action); 93 } 94 95 function* verifySignature(action: types.SignLocalMessageSucceededAction): SagaIterator { 96 const success = yield call(verifySignedMessage, action.payload); 97 98 if (success) { 99 yield put( 100 notificationsActions.showNotification( 101 'success', 102 translateRaw('SIGN_MSG_SUCCESS', { $address: action.payload.address }) 103 ) 104 ); 105 } else { 106 yield put(actions.signMessageFailed()); 107 yield put(notificationsActions.showNotification('danger', translateRaw('ERROR_38'))); 108 } 109 } 110 111 export const signing = [ 112 takeEvery(types.MessageActions.SIGN_REQUESTED, handleMessageRequest), 113 takeEvery(types.MessageActions.SIGN_LOCAL_SUCCEEDED, verifySignature) 114 ]; 115 116 export function* messageSaga(): SagaIterator { 117 yield all([...signing]); 118 }