diff --git a/src/components/controls/index.js b/src/components/controls/index.js index 8f29200..07076a0 100644 --- a/src/components/controls/index.js +++ b/src/components/controls/index.js @@ -1,8 +1,8 @@ import React from 'react'; -import injectSheet from 'react-jss'; import PropTypes from 'prop-types'; -import View from '../view'; +import injectSheet from 'react-jss'; import { MdArrowForward } from 'react-icons/md'; +import View from '../view'; const styles = theme => ({ wrapper: { @@ -85,14 +85,14 @@ Controls.propTypes = { classes: PropTypes.object.isRequired, text: PropTypes.string, error: PropTypes.bool, - errorText: PropTypes.string, errorAction: PropTypes.func, + errorText: PropTypes.string, onPress: PropTypes.func, loading: PropTypes.bool, loadingText: PropTypes.string, loadingStyle: PropTypes.string, loadingRender: PropTypes.func, - errorRender: PropTypes.func, + errorRender: PropTypes.node, }; export default injectSheet(styles)(Controls); diff --git a/src/constants/index.js b/src/constants/index.js index f5d50a4..033d815 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -4,6 +4,19 @@ const capitalizeFirstLetter = input => { return input.charAt(0).toUpperCase() + input.slice(1); }; +export const SwapUpdateEvent = { + InvoicePaid: 'invoice.paid', + InvoiceSettled: 'invoice.settled', + InvoiceFailedToPay: 'invoice.failedToPay', + + TransactionRefunded: 'transaction.refunded', + TransactionConfirmed: 'transaction.confirmed', +}; + +/** + * Values from the environment + */ + // API endpoint export const boltzApi = process.env.REACT_APP_BOLTZ_API; diff --git a/src/views/reverse/reverseActions.js b/src/views/reverse/reverseActions.js index d3f5e32..d68053c 100644 --- a/src/views/reverse/reverseActions.js +++ b/src/views/reverse/reverseActions.js @@ -2,16 +2,16 @@ import axios from 'axios'; import EventSource from 'eventsource'; import { Transaction, ECPair, address } from 'bitcoinjs-lib'; import { detectSwap, constructClaimTransaction } from 'boltz-core'; -import { boltzApi } from '../../constants'; import * as actionTypes from '../../constants/actions'; +import { boltzApi, SwapUpdateEvent } from '../../constants'; import { toSatoshi, - getHexBuffer, getNetwork, + getHexBuffer, getFeeEstimation, } from '../../scripts/utils'; -let latestSwapStatus = ''; +let latestSwapEvent = ''; export const initReverseSwap = state => ({ type: actionTypes.INIT_REVERSE_SWAP, @@ -94,7 +94,7 @@ export const startReverseSwap = (swapInfo, nextStage, timelockExpired) => { }; }; -const claimTransaction = (swapInfo, response, preimage, feeEstimation) => { +const getClaimTransaction = (swapInfo, response, preimage, feeEstimation) => { const redeemScript = getHexBuffer(response.redeemScript); const lockupTransaction = Transaction.fromHex(response.lockupTransaction); @@ -114,7 +114,7 @@ const claimTransaction = (swapInfo, response, preimage, feeEstimation) => { ); }; -const handleSwapStatus = ( +const handleReverseSwapStatus = ( data, source, dispatch, @@ -123,38 +123,56 @@ const handleSwapStatus = ( swapInfo, response ) => { - const message = data.message; + const event = data.event; - if (message === latestSwapStatus) { + // If this function is called with the data from the GET endpoint "/swapstatus" + // it could be that the received event has already been handled + if (event === latestSwapEvent) { return; } else { - latestSwapStatus = message; + latestSwapEvent = event; } - if (message.startsWith('Transaction confirmed')) { - dispatch(setReverseSwapStatus('Waiting for invoice to be paid...')); - nextStage(); - } else if (data.message.startsWith('Refunded lockup transaction')) { - source.close(); - dispatch(timelockExpired()); - } else if (!message.startsWith('Could not find swap with id')) { - source.close(); - dispatch( - getFeeEstimation(feeEstimation => { - const claimTx = claimTransaction( - swapInfo, - response, - data.preimage, - feeEstimation - ); - dispatch( - broadcastClaim(swapInfo.quote, claimTx.toHex(), () => { - dispatch(reverseSwapResponse(true, response)); - nextStage(); - }) - ); - }) - ); + switch (event) { + case SwapUpdateEvent.TransactionConfirmed: + dispatch(setReverseSwapStatus('Waiting for invoice to be paid...')); + nextStage(); + break; + + case SwapUpdateEvent.TransactionRefunded: + source.close(); + dispatch(timelockExpired()); + break; + + case SwapUpdateEvent.InvoiceSettled: + source.close(); + + dispatch( + getFeeEstimation(feeEstimation => { + const claimTransaction = getClaimTransaction( + swapInfo, + response, + data.preimage, + feeEstimation + ); + + dispatch( + broadcastClaimTransaction( + swapInfo.quote, + claimTransaction.toHex(), + () => { + dispatch(reverseSwapResponse(true, response)); + nextStage(); + } + ) + ); + }) + ); + break; + + default: + console.log(`Unknown swap status: ${data}`); + break; } }; @@ -196,7 +214,7 @@ const startListening = ( timelockExpired ); - handleSwapStatus( + handleReverseSwapStatus( statusReponse.data, source, dispatch, @@ -210,7 +228,7 @@ const startListening = ( }; source.onmessage = event => { - handleSwapStatus( + handleReverseSwapStatus( JSON.parse(event.data), source, dispatch, @@ -222,7 +240,7 @@ const startListening = ( }; }; -const broadcastClaim = (currency, claimTransaction, cb) => { +const broadcastClaimTransaction = (currency, claimTransaction, cb) => { const url = `${boltzApi}/broadcasttransaction`; return dispatch => { axios diff --git a/src/views/swap/swap.js b/src/views/swap/swap.js index 638b3cb..9de4fcf 100644 --- a/src/views/swap/swap.js +++ b/src/views/swap/swap.js @@ -123,7 +123,7 @@ const Swap = ({ loading={swapStatus.pending} error={swapStatus.error} errorText={swapStatus.message} - errorAction={() => startSwap(swapInfo, props.nextStage)} + errorRender={() => {}} loadingRender={() => } onPress={() => { props.nextStage(); diff --git a/src/views/swap/swapActions.js b/src/views/swap/swapActions.js index a7eafce..d81fb95 100644 --- a/src/views/swap/swapActions.js +++ b/src/views/swap/swapActions.js @@ -1,6 +1,6 @@ import axios from 'axios'; import EventSource from 'eventsource'; -import { boltzApi } from '../../constants'; +import { boltzApi, SwapUpdateEvent } from '../../constants'; import * as actionTypes from '../../constants/actions'; export const completeSwap = () => { @@ -74,35 +74,54 @@ export const startSwap = (swapInfo, cb) => { }; }; +const handleSwapStatus = (data, source, dispatch, callback) => { + const event = data.event; + + switch (event) { + case SwapUpdateEvent.TransactionConfirmed: + dispatch( + setSwapStatus({ + pending: true, + message: 'Waiting for invoice to be paid...', + }) + ); + break; + + case SwapUpdateEvent.InvoiceFailedToPay: + source.close(); + dispatch( + setSwapStatus({ + error: true, + pending: false, + message: 'Could not pay invoice. Please refund your coins.', + }) + ); + break; + + case SwapUpdateEvent.InvoicePaid: + source.close(); + callback(); + break; + + default: + console.log(`Unknown swap status: ${data}`); + break; + } +}; + export const startListening = (dispatch, swapId, callback) => { const source = new EventSource(`${boltzApi}/streamswapstatus?id=${swapId}`); - let message = { - pending: true, - message: 'Waiting for one confirmation...', - }; - - dispatch(setSwapStatus(message)); + dispatch( + setSwapStatus({ + pending: true, + message: 'Waiting for one confirmation...', + }) + ); source.onmessage = event => { const data = JSON.parse(event.data); - if (data.message.startsWith('Invoice paid:')) { - source.close(); - callback(); - } else if (data.message.startsWith('Transaction confirmed:')) { - message = { - pending: true, - message: 'Waiting for invoice to be paid...', - }; - } else { - message = { - error: true, - pending: true, - message: 'Boltz could not find the transaction', - }; - } - - dispatch(setSwapStatus(message)); + handleSwapStatus(data, source, dispatch, callback); }; };