Skip to content
Open
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/app/modules/main/wallet_section/send_new/module.nim
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ method authenticateAndTransfer*(self: Module, uuid: string, fromAddr: string) =
self.controller.authenticate()

method onUserAuthenticated*(self: Module, password: string, pin: string) =
if password.len == 0:
if password.len == 0 and pin.len == 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A password should be set in both cases, regular/keycard user, while pin only for the keycard user, that's why only password was checked. It's not incorrect this way ofc.

self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = authenticationCanceled)
self.clearTmpData()
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ method resolveKeycardNextState*(self: InsertKeycardState, keycardFlowType: strin
return nil
if keycardFlowType == ResponseTypeValueCardInserted:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))

# Special handling for LoadAccount flow - return to the state we came from
# (RepeatPin or PinSet) to continue waiting for ENTER_MNEMONIC event
if (self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase) and
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount and
not self.getBackState.isNil:
let backStateType = self.getBackState.stateType
if backStateType == StateType.RepeatPin or backStateType == StateType.PinSet:
# Return to the previous state to continue waiting for mnemonic entry
return self.getBackState

# Default behavior for other flows
if self.flowType == FlowType.SetupNewKeycard:
return createState(StateType.KeycardInserted, self.flowType, self.getBackState)
return createState(StateType.KeycardInserted, self.flowType, nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,28 @@ method executeCancelCommand*(self: PinSetState, controller: Controller) =
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

method resolveKeycardNextState*(self: PinSetState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
# Handle temporary card disconnection during LoadAccount flow (after card initialization)
# This can happen if the user hasn't tapped "Continue" yet and the card disconnects
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase:
# INSERT_CARD during LoadAccount flow means card is reconnecting after initialization
if keycardFlowType == ResponseTypeValueInsertCard and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection and
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
# Don't cancel the flow - transition to InsertKeycard state and wait for reconnection
controller.reRunCurrentFlowLater()
return createState(StateType.InsertKeycard, self.flowType, self)
# CARD_INSERTED after temporary disconnection - stay in PinSet and continue
if keycardFlowType == ResponseTypeValueCardInserted and
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
# Card reconnected successfully, stay in PinSet
return nil

# No specific handling needed - this state transitions via primary button
return nil
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ method executeCancelCommand*(self: RepeatPinState, controller: Controller) =

method resolveKeycardNextState*(self: RepeatPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
# Handle temporary card disconnection during LoadAccount flow (after card initialization)
# This happens on Android/iOS when card is disconnected and needs to be re-detected
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase:
# INSERT_CARD during LoadAccount flow means card is reconnecting after initialization
if keycardFlowType == ResponseTypeValueInsertCard and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection and
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
# Don't cancel the flow - transition to InsertKeycard state and wait for reconnection
controller.reRunCurrentFlowLater()
return createState(StateType.InsertKeycard, self.flowType, self)
# CARD_INSERTED after temporary disconnection - stay in RepeatPin and continue waiting
if keycardFlowType == ResponseTypeValueCardInserted and
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
# Card reconnected successfully, continue waiting for ENTER_MNEMONIC event
return nil

let state = ensureReaderAndCardPresence(self, keycardFlowType, keycardEvent, controller)
if not state.isNil:
return state
Expand Down
7 changes: 7 additions & 0 deletions src/app_service/service/keycard/service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,15 @@ QtObject:
return seedPhrase

proc updateLocalPayloadForCurrentFlow(self: Service, obj: JsonNode, cleanBefore = false) {.featureGuard(KEYCARD_ENABLED).} =
# CRITICAL FIX: Check if obj is the same reference as setPayloadForCurrentFlow
# This happens when onTimeout calls startFlow(self.setPayloadForCurrentFlow)
# If we iterate and modify the same object, the iterator gets corrupted!
if cast[pointer](obj) == cast[pointer](self.setPayloadForCurrentFlow):
return

if cleanBefore:
self.setPayloadForCurrentFlow = %* {}

for k, v in obj:
self.setPayloadForCurrentFlow[k] = v

Expand Down