Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: abort collaborative transaction #497

Merged
merged 6 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/Jam.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export default function Jam() {
setIsLoading(true)

const abortCtrl = new AbortController()
return Api.getSchedulerStop({ signal: abortCtrl.signal, walletName: wallet.name, token: wallet.token })
return Api.getTakerStop({ signal: abortCtrl.signal, walletName: wallet.name, token: wallet.token })
.then((res) => (res.ok ? true : Api.Helper.throwError(res, t('scheduler.error_stopping_schedule_failed'))))
.then((_) => setCollaborativeOperationRunning(false))
.then((_) =>
Expand Down
82 changes: 65 additions & 17 deletions src/components/Send.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ export default function Send() {
[isInitializing, waitForUtxosToBeSpent]
)

const [showConfirmInputsModal, setShowConfirmInputsModal] = useState(false)
const [showConfirmAbortModal, setShowConfirmAbortModal] = useState(false)
const [showConfirmSendModal, setShowConfirmSendModal] = useState(false)
const submitButtonRef = useRef(null)

useEffect(() => {
Expand Down Expand Up @@ -498,6 +499,34 @@ export default function Send() {
return success
}

useEffect(() => {
// hide the abort modal, if a user wants to abort a running transaction,
// but the transaction failed or succeeded in the meantime
if (showConfirmAbortModal && !isCoinjoinInProgress) {
setShowConfirmAbortModal(false)
}
}, [isCoinjoinInProgress, showConfirmAbortModal])

const abortCoinjoin = async () => {
if (!isCoinjoinInProgress) {
setShowConfirmAbortModal(false)
return
}

if (!showConfirmAbortModal) {
setShowConfirmAbortModal(true)
return
}

setShowConfirmAbortModal(false)
setAlert(null)

const abortCtrl = new AbortController()
return Api.getTakerStop({ signal: abortCtrl.signal, walletName: wallet.name, token: wallet.token }).catch((err) => {
setAlert({ variant: 'danger', message: err.message })
})
}

const onSubmit = async (e) => {
e.preventDefault()

Expand All @@ -509,12 +538,12 @@ export default function Send() {
const isValid = formIsValid

if (isValid) {
if (!showConfirmInputsModal) {
setShowConfirmInputsModal(true)
if (!showConfirmSendModal) {
setShowConfirmSendModal(true)
return
}

setShowConfirmInputsModal(false)
setShowConfirmSendModal(false)

const counterparties = parseInt(numCollaborators, 10)

Expand Down Expand Up @@ -652,8 +681,17 @@ export default function Send() {
</Link>
)}
{isCoinjoinInProgress && (
<rb.Alert variant="info" className="mb-4">
<rb.Alert variant="info" className="mb-4 d-flex align-items-center">
{t('send.text_coinjoin_already_running')}

<rb.Button
variant={'outline-light'}
className="ms-auto"
disabled={showConfirmAbortModal}
onClick={() => abortCoinjoin()}
>
{t('global.abort')}
</rb.Button>
</rb.Alert>
)}
</>
Expand Down Expand Up @@ -893,10 +931,20 @@ export default function Send() {
t('send.button_send_without_improved_privacy')
)}
</rb.Button>

<ConfirmModal
isShown={showConfirmAbortModal}
title={t('send.confirm_abort_modal.title')}
onCancel={() => setShowConfirmAbortModal(false)}
onConfirm={() => abortCoinjoin()}
>
{t('send.confirm_abort_modal.text_body')}
</ConfirmModal>

<ConfirmModal
isShown={showConfirmInputsModal}
title={t('send.confirm_modal.title')}
onCancel={() => setShowConfirmInputsModal(false)}
isShown={showConfirmSendModal}
title={t('send.confirm_send_modal.title')}
onCancel={() => setShowConfirmSendModal(false)}
onConfirm={() => {
submitButtonRef.current?.click()
}}
Expand All @@ -905,36 +953,36 @@ export default function Send() {
<rb.Row className="mt-2 mb-3">
<rb.Col xs={12} className="text-center">
{isCoinjoin ? (
<strong className="text-success">{t('send.confirm_modal.text_collaborative_tx_enabled')}</strong>
<strong className="text-success">{t('send.confirm_send_modal.text_collaborative_tx_enabled')}</strong>
) : (
<strong className="text-danger">{t('send.confirm_modal.text_collaborative_tx_disabled')}</strong>
<strong className="text-danger">{t('send.confirm_send_modal.text_collaborative_tx_disabled')}</strong>
)}
</rb.Col>
</rb.Row>
<rb.Row>
<rb.Col xs={3} className="text-end">
<strong>{t('send.confirm_modal.label_source_jar')}</strong>
<strong>{t('send.confirm_send_modal.label_source_jar')}</strong>
</rb.Col>
<rb.Col xs={9} className="text-start">
{t('send.confirm_modal.text_source_jar', { jarId: jarInitial(account) })}
{t('send.confirm_send_modal.text_source_jar', { jarId: jarInitial(account) })}
</rb.Col>
</rb.Row>
<rb.Row>
<rb.Col xs={3} className="text-end">
<strong>{t('send.confirm_modal.label_recipient')}</strong>
<strong>{t('send.confirm_send_modal.label_recipient')}</strong>
</rb.Col>
<rb.Col xs={9} className="text-start text-break slashed-zeroes">
{destination}
</rb.Col>
</rb.Row>
<rb.Row>
<rb.Col xs={3} className="text-end">
<strong>{t('send.confirm_modal.label_amount')}</strong>
<strong>{t('send.confirm_send_modal.label_amount')}</strong>
</rb.Col>
<rb.Col xs={9} className="text-start">
{isSweep ? (
<div className="d-flex justify-content-start align-items-center">
<Trans i18nKey="send.confirm_modal.text_sweep_balance">
<Trans i18nKey="send.confirm_send_modal.text_sweep_balance">
Sweep
<Balance
valueString={amountFieldValue().toString()}
Expand All @@ -946,7 +994,7 @@ export default function Send() {
placement="right"
overlay={
<rb.Popover>
<rb.Popover.Body>{t('send.confirm_modal.text_sweep_info_popover')}</rb.Popover.Body>
<rb.Popover.Body>{t('send.confirm_send_modal.text_sweep_info_popover')}</rb.Popover.Body>
</rb.Popover>
}
>
Expand All @@ -967,7 +1015,7 @@ export default function Send() {
{isCoinjoin && (
<rb.Row>
<rb.Col xs={3} className="text-end">
<strong>{t('send.confirm_modal.label_num_collaborators')}</strong>
<strong>{t('send.confirm_send_modal.label_num_collaborators')}</strong>
</rb.Col>
<rb.Col xs={9} className="text-start">
{numCollaborators}
Expand Down
7 changes: 6 additions & 1 deletion src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"next": "Next",
"back": "Back",
"close": "Close",
"abort": "Abort",
"table": {
"pagination": {
"items_per_page": {
Expand Down Expand Up @@ -208,7 +209,7 @@
"sweep_amount_breakdown_frozen_balance": "Frozen or locked balance",
"sweep_amount_breakdown_estimated_amount": "Estimated amount to be sent",
"sweep_amount_breakdown_explanation": "A sweep transaction will consume all UTXOs of a mixdepth leaving no coins behind except those that have been <1>frozen</1> or <3>time-locked</3>. Onchain transaction fees and market maker fees will be deducted from the amount so as to leave zero change. The exact transaction amount can only be calculated by JoinMarket at the point when the transaction is made. Therefore the estimated amount shown might deviate from the actually sent amount. Refer to the <5>JoinMarket documentation</5> for more details.",
"confirm_modal": {
"confirm_send_modal": {
"title": "Confirm payment",
"label_amount": "Amount",
"text_sweep_balance": "Sweep whole jar (<1></1>)",
Expand All @@ -220,6 +221,10 @@
"text_collaborative_tx_enabled": "Payment with privacy improvement",
"text_collaborative_tx_disabled": "Payment without privacy improvement"
},
"confirm_abort_modal": {
"title": "Abort payment",
"text_body": "Are you sure you want to abort the collaborative transaction?"
},
"coinjoin_precondition": {
"hint_missing_utxos": "To execute a collaborative transaction you need UTXOs with {{ minConfirmations }} or more confirmations in the source jar. $t(send.coinjoin_precondition.nested_hint_fund_jar, {\"count\": {{ minConfirmations }} })",
"hint_missing_confirmations": "A collaborative transaction requires your UTXOs to have {{ minConfirmations }} or more confirmations. $t(send.coinjoin_precondition.nested_hint_wait_for_block, {\"count\": {{ amountOfMissingConfirmations }} })",
Expand Down
4 changes: 2 additions & 2 deletions src/libs/JmWalletApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ const postSchedulerStart = async ({ token, signal, walletName }: WalletRequestCo
})
}

const getSchedulerStop = async ({ token, signal, walletName }: WalletRequestContext) => {
const getTakerStop = async ({ token, signal, walletName }: WalletRequestContext) => {
return await fetch(`${basePath()}/v1/wallet/${encodeURIComponent(walletName)}/taker/stop`, {
headers: { ...Helper.buildAuthHeader(token) },
signal,
Expand Down Expand Up @@ -414,7 +414,7 @@ export {
postConfigGet,
getWalletSeed,
postSchedulerStart,
getSchedulerStop,
getTakerStop,
getSchedule,
Helper,
}