diff --git a/services/opencode/src/main/kotlin/com/getcode/opencode/internal/transactors/GiveBillTransactor.kt b/services/opencode/src/main/kotlin/com/getcode/opencode/internal/transactors/GiveBillTransactor.kt index 4299980af..9c90a8a82 100644 --- a/services/opencode/src/main/kotlin/com/getcode/opencode/internal/transactors/GiveBillTransactor.kt +++ b/services/opencode/src/main/kotlin/com/getcode/opencode/internal/transactors/GiveBillTransactor.kt @@ -112,6 +112,10 @@ internal class GiveBillTransactor( * @return the confirmed [TransactionMetadata.SendPublicPayment] on success. */ suspend fun start(): Result { + if (!scope.isActive) { + return logAndFail(GiveTransactorError.Other(message = "Transactor was disposed")) + } + val ownerKey = owner ?: return logAndFail(GiveTransactorError.Other(message = "No owner key. Did you call with() first?")) val desiredToken = token @@ -212,13 +216,13 @@ internal class GiveBillTransactor( /** Cancels the coroutine scope and clears all held state. */ fun dispose() { + scope.cancel() owner = null presentationData = BillPresentationData(emptyList(), emptyList()) rendezvousKey = null receivingAccount = null token = null providedVerifiedState = null - scope.cancel() } sealed class GiveTransactorError( diff --git a/services/opencode/src/main/kotlin/com/getcode/opencode/managers/BillTransactionManager.kt b/services/opencode/src/main/kotlin/com/getcode/opencode/managers/BillTransactionManager.kt index 6f7857117..e6f3b7a44 100644 --- a/services/opencode/src/main/kotlin/com/getcode/opencode/managers/BillTransactionManager.kt +++ b/services/opencode/src/main/kotlin/com/getcode/opencode/managers/BillTransactionManager.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel +import kotlinx.coroutines.ensureActive import kotlinx.coroutines.launch import java.util.Timer import java.util.TimerTask @@ -113,6 +114,10 @@ class BillTransactionManager @Inject constructor( present(transactor.presentationData) presentBillForGive(onTimeout) + // If cancelAwaitForGrab() fired between present() and here, + // bail out before start() reads fields that dispose() may have nulled. + ensureActive() + transactor.start() .onSuccess { childScope.cancel()