import { instantiate } from "ivy-bitcoin" ; import { addParameterInput } from "../inputs/data" ; import { ContractParameterType , HashFunction , Input , InputMap } from "../inputs/types" ; import { getParameterIds } from "../templates/selectors" ; import { getInputMap } from "../templates/selectors" ; import { Template } from "../templates/types" ; import { ContractsState } from "./types" ; import { AppState } from "../app/types" ; import { addDefaultInput } from "../inputs/data" ; import { Contract } from "./types" ; import { CREATE_CONTRACT , SET_CLAUSE_INDEX , SHOW_UNLOCK_INPUT_ERRORS , SPEND_CONTRACT , UPDATE_CLAUSE_INPUT , UPDATE_ERROR } from "./actions" ; import { Contract as IvyContract , TemplateClause , Transaction } from "ivy-bitcoin" ; export const INITIAL_STATE = { contractMap : { } , idList : [ ] , spentIdList : [ ] , spendContractId : "" , selectedClauseIndex : 0 , showUnlockInputErrors : false , error : undefined } ; export interface ClauseMap { [ s ] : TemplateClause ; } function generateClauseMap ( template ) { const clauseMap = { } ; for ( const clause of template . clauses ) { clauseMap [ clause . name ] = clause ; } return clauseMap ; } function generateSpendInputMap ( template ) { const clauseParameterIds = { } ; const inputs = [ ] ; for ( const clause of template . clauses ) { clauseParameterIds [ clause . name ] = clause . parameters . map ( param => "clauseParameters." + clause . name + "." + param . name ) ; for ( const parameter of clause . parameters ) { addParameterInput ( inputs , parameter . valueType , "clauseParameters." + clause . name + "." + parameter . name ) ; } } addDefaultInput ( inputs , "lockTimeInput" , "transactionDetails" ) ; addDefaultInput ( inputs , "sequenceNumberInput" , "transactionDetails" ) ; const spendInputsById = { } ; for ( const input of inputs ) { spendInputsById [ input . name ] = input ; } return spendInputsById ; } export default function reducer ( state = INITIAL_STATE , action ) { switch ( action . type ) { case SPEND_CONTRACT : { const contract = action . contract ; const contractId = contract . id ; return { ... state , contractMap : { ... state . contractMap , [ contractId ] : { ... contract , unlockTxid : action . unlockTxid } } , idList : state . idList . filter ( id => id !== contractId ) , spentIdList : [ contractId , ... state . spentIdList ] , error : undefined } ; } case SHOW_UNLOCK_INPUT_ERRORS : { return { ... state , showUnlockInputErrors : action . result } ; } case CREATE_CONTRACT : { const instantiated = action . instantiated ; if ( instantiated . fundingTransaction === undefined ) { throw new Error ( "did not expect funding transaction to be undefined" ) ; } const template = action . template ; const spendInputMap = generateSpendInputMap ( template ) ; const clauseNames = template . clauses . map ( clause => clause . name ) ; const clauseMap = generateClauseMap ( template ) ; const tx = instantiated . fundingTransaction ; const contract = { inputMap : action . inputMap as InputMap , id : tx . hash , spendInputMap , unlockTxid : undefined , clauseMap , instantiated } ; const contractId = contract . id ; return { ... state , contractMap : { ... state . contractMap , [ contractId ] : contract } , idList : [ ... state . idList , contractId ] } ; } case UPDATE_CLAUSE_INPUT : { const contractId = action . contractId as string ; const oldContract = state . contractMap [ action . contractId ] ; const oldSpendInputMap = oldContract . spendInputMap ; const oldInput = oldSpendInputMap [ action . name ] ; if ( oldInput === undefined ) { throw new Error ( "unexpectedly undefined clause input" ) ; } return { ... state , contractMap : { ... state . contractMap , [ action . contractId ] : { ... oldContract , spendInputMap : { ... oldSpendInputMap , [ action . name ] : { ... oldInput , value : action . newValue } } } } } ; } case SET_CLAUSE_INDEX : { return { ... state , selectedClauseIndex : action . selectedClauseIndex } ; } case UPDATE_ERROR : { return { ... state , error : action . error } ; } case "@@router/LOCATION_CHANGE" : const path = action . payload . pathname . split ( "/" ) ; if ( path . length < 2 ) { return state ; } const pathId = path . pop ( ) ; if ( path . pop ( ) === "unlock" ) { return { ... state , spendContractId : pathId , selectedClauseIndex : 0 } ; } return state ; default : return state ; } }