Usage docs Reference Examples

src/tracking/transactions/transactionsRed.js

import * as transactionsAT from './AT';
import mappedReducer from '../../util/mapped-reducer';

const initialState = {
  // empty initially
};

// TX flow:
//
//     *creation*         = User calls tx method
//  -> [send]             = TX_SEND is started
//  -> [sent]             = tx is added into tracker store, TX_SENT.
//                           Web3provider needs to broadcast now
//                           (e.g. user signs+sends with Metamask)
//  -> [broadcast]        = Web3provider answered, we have a hash now, TX is in pool. TX_BROADCAST.
//  -> [pending]          = Waiting for tx to get mined
//  -> *confirmation*     = TX is mined, now we have to wait for the web3 provider to tell us.
//  -> [receipt]          = Web3provider tells us the tx was processed.
//                           Receipt is handled with TX_RECEIPT.
//                           The receipt is being processed now.
//  -> [failed/success]   = ReDApp fires an TX_SUCCESS/TX_FAILED depending on the receipt.
//                           Note: TX_FAILED can also be fired without receipt,
//                           meaning that the TX failed before being processed.
//                           It fails with receipt when it ran out of gas.
//  -> *more blocks mined on top*
//  -> [confirmation, for each block] = Receipt may change, web3 tells us about the next 12 blocks.
//                           But what we're really interested in is if the receipt changes,
//                           in which case there should be another confirmation
//                           from web3 with number 0. In this case, the TX_RECEIPT is fired again.
//
// *something* = implicit state, no events
// [something] = explicit, fires an event
//
// Note that pending is an implicit state,
//  and that the first confirmation won't be noticed before getting the first receipt,
//  hence it not being a Redux event.

// simple util function that replicates previous state and replace the specific transaction state.
const updateTx = (state, txID, txState) => ({
  ...state,
  [txID]: txState
});

const mapping = {
  [transactionsAT.TX_SENT]: (state, { txID, data }) => updateTx(state, txID,
    {...state[txID], status: 'sent', receipt: { input: data } }),

  [transactionsAT.SEND_TX_FAILED]: (state, { txID, err }) => updateTx(state, txID,
    {...state[txID], status: 'send_failed', err }),

  [transactionsAT.TX_BROADCAST]: (state, { txID, txHash }) => updateTx(state, txID,
    {...state[txID], status: 'broadcast', hash: txHash }),

  [transactionsAT.TX_FAILED]: (state, { txID, receipt }) => updateTx(state, txID,
    {...state[txID], status: 'failed', receipt: { input: (state[txID].receipt || {}).input, ...(receipt || {}) }}),

  [transactionsAT.TX_RECEIPT]: (state, { txID, receipt }) => updateTx(state, txID,
    {...state[txID], receipt: { input: (state[txID].receipt || {}).input, ...(receipt || {}) }, status: receipt.blockNumber === null ? 'pending' : 'success'}),

  [transactionsAT.FORGET_TX]: (state, { txID }) => {
    const res = {...state};
    delete res[txID];
    return res;
  }
};

/**
 * Transactions reducer of redapp.
 * @type {ReduxReducer}
 */
export default mappedReducer(mapping, initialState);