fix(@desktop/keycard): can't create accounts if the main account was restored in using an existing keycard account
`I already use Status` -> `Login with Keycard` flow is enabled. Fixes: #7867
This commit is contained in:
parent
c8c51a8621
commit
2c03146309
|
@ -266,6 +266,7 @@ proc init*(self: Controller) =
|
|||
password = args.keyUid
|
||||
let data = SharedKeycarModuleArgs(uniqueIdentifier: self.authenticateUserFlowRequestedBy,
|
||||
password: password,
|
||||
pin: args.pin,
|
||||
keyUid: args.keyUid,
|
||||
txR: args.txR,
|
||||
txS: args.txS,
|
||||
|
|
|
@ -169,7 +169,7 @@ proc newModule*[T](
|
|||
result.walletSectionModule = wallet_section_module.newModule(
|
||||
result, events, tokenService,
|
||||
transactionService, collectible_service, walletAccountService,
|
||||
settingsService, savedAddressService, networkService, accountsService
|
||||
settingsService, savedAddressService, networkService, accountsService, keycardService
|
||||
)
|
||||
result.browserSectionModule = browser_section_module.newModule(
|
||||
result, events, bookmarkService, settingsService, networkService,
|
||||
|
@ -950,17 +950,18 @@ method onStatusUrlRequested*[T](self: Module[T], action: StatusUrlAction, commun
|
|||
# self.setActiveSection(item)
|
||||
# self.browserSectionModule.openUrl(url)
|
||||
|
||||
proc isSharedKeycardModuleFlowRunning[T](self: Module[T]): bool =
|
||||
return not self.keycardSharedModule.isNil
|
||||
|
||||
method getKeycardSharedModule*[T](self: Module[T]): QVariant =
|
||||
return self.keycardSharedModule.getModuleAsVariant()
|
||||
if self.isSharedKeycardModuleFlowRunning():
|
||||
return self.keycardSharedModule.getModuleAsVariant()
|
||||
|
||||
proc createSharedKeycardModule[T](self: Module[T]) =
|
||||
self.keycardSharedModule = keycard_shared_module.newModule[Module[T]](self, UNIQUE_MAIN_MODULE_IDENTIFIER,
|
||||
self.events, self.keycardService, self.settingsService, self.privacyService, self.accountsService,
|
||||
self.walletAccountService, self.keychainService)
|
||||
|
||||
proc isSharedKeycardModuleFlowRunning[T](self: Module[T]): bool =
|
||||
return not self.keycardSharedModule.isNil
|
||||
|
||||
method onSharedKeycarModuleFlowTerminated*[T](self: Module[T], lastStepInTheCurrentFlow: bool) =
|
||||
if self.isSharedKeycardModuleFlowRunning():
|
||||
self.view.emitDestroyKeycardSharedModuleFlow()
|
||||
|
|
|
@ -8,6 +8,7 @@ import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_modu
|
|||
import ../../../../core/eventemitter
|
||||
|
||||
const UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER* = "WalletSection-AccountsModule"
|
||||
const UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER* = "WalletSection-AccountsModule-Authentication"
|
||||
|
||||
type
|
||||
Controller* = ref object of RootObj
|
||||
|
@ -34,21 +35,37 @@ proc delete*(self: Controller) =
|
|||
proc init*(self: Controller) =
|
||||
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
|
||||
let args = SharedKeycarModuleArgs(e)
|
||||
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER:
|
||||
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER:
|
||||
return
|
||||
self.delegate.onUserAuthenticated(args.password)
|
||||
|
||||
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED_AND_WALLET_ADDRESS_GENERATED) do(e: Args):
|
||||
let args = SharedKeycarModuleUserAuthenticatedAndWalletAddressGeneratedArgs(e)
|
||||
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER:
|
||||
return
|
||||
self.delegate.onUserAuthenticatedAndWalletAddressGenerated(args.address, args.publicKey, args.derivedFrom, args.password)
|
||||
|
||||
self.events.on(SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_DETAILS_FETCHED) do(e: Args):
|
||||
let args = DerivedAddressesArgs(e)
|
||||
var derivedAddress: DerivedAddressDto
|
||||
if args.derivedAddresses.len > 0:
|
||||
derivedAddress = args.derivedAddresses[0]
|
||||
self.delegate.addressDetailsFetched(derivedAddress, args.error)
|
||||
|
||||
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
|
||||
return self.walletAccountService.getWalletAccounts()
|
||||
|
||||
proc generateNewAccount*(self: Controller, password: string, accountName: string, color: string, emoji: string, path: string, derivedFrom: string): string =
|
||||
return self.walletAccountService.generateNewAccount(password, accountName, color, emoji, path, derivedFrom)
|
||||
proc generateNewAccount*(self: Controller, password: string, accountName: string, color: string, emoji: string,
|
||||
path: string, derivedFrom: string, skipPasswordVerification: bool): string =
|
||||
return self.walletAccountService.generateNewAccount(password, accountName, color, emoji, path, derivedFrom, skipPasswordVerification)
|
||||
|
||||
proc addAccountsFromPrivateKey*(self: Controller, privateKey: string, password: string, accountName: string, color: string, emoji: string): string =
|
||||
return self.walletAccountService.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji)
|
||||
proc addAccountsFromPrivateKey*(self: Controller, privateKey: string, password: string, accountName: string, color: string,
|
||||
emoji: string, skipPasswordVerification: bool): string =
|
||||
return self.walletAccountService.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji, skipPasswordVerification)
|
||||
|
||||
proc addAccountsFromSeed*(self: Controller, seedPhrase: string, password: string, accountName: string, color: string, emoji: string, path: string): string =
|
||||
return self.walletAccountService.addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path)
|
||||
proc addAccountsFromSeed*(self: Controller, seedPhrase: string, password: string, accountName: string, color: string,
|
||||
emoji: string, path: string, skipPasswordVerification: bool): string =
|
||||
return self.walletAccountService.addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path, skipPasswordVerification)
|
||||
|
||||
proc addWatchOnlyAccount*(self: Controller, address: string, accountName: string, color: string, emoji: string): string =
|
||||
return self.walletAccountService.addWatchOnlyAccount(address, accountName, color, emoji)
|
||||
|
@ -56,6 +73,9 @@ proc addWatchOnlyAccount*(self: Controller, address: string, accountName: string
|
|||
proc deleteAccount*(self: Controller, address: string) =
|
||||
self.walletAccountService.deleteAccount(address)
|
||||
|
||||
proc fetchDerivedAddressDetails*(self: Controller, address: string) =
|
||||
self.walletAccountService.fetchDerivedAddressDetails(address)
|
||||
|
||||
method getDerivedAddressList*(self: Controller, password: string, derivedFrom: string, path: string, pageSize: int, pageNumber: int)=
|
||||
self.walletAccountService.getDerivedAddressList(password, derivedFrom, path, pageSize, pageNumber)
|
||||
|
||||
|
@ -70,6 +90,14 @@ proc validSeedPhrase*(self: Controller, seedPhrase: string): bool =
|
|||
return err.len == 0
|
||||
|
||||
proc authenticateUser*(self: Controller, keyUid = "") =
|
||||
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER,
|
||||
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER,
|
||||
keyUid: keyUid)
|
||||
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
|
||||
|
||||
proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] =
|
||||
return self.walletAccountService.getAllMigratedKeyPairs()
|
||||
|
||||
proc addWalletAccount*(self: Controller, name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid, accountType,
|
||||
color, emoji: string): string =
|
||||
return self.walletAccountService.addWalletAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid,
|
||||
accountType, color, emoji)
|
|
@ -7,24 +7,32 @@ type
|
|||
iconName: string
|
||||
generatedModel: Model
|
||||
derivedfrom: string
|
||||
keyUid: string
|
||||
migratedToKeycard: bool
|
||||
|
||||
proc initGeneratedWalletItem*(
|
||||
name: string,
|
||||
iconName: string,
|
||||
generatedModel: Model,
|
||||
derivedfrom: string
|
||||
derivedfrom: string,
|
||||
keyUid: string,
|
||||
migratedToKeycard: bool
|
||||
): GeneratedWalletItem =
|
||||
result.name = name
|
||||
result.iconName = iconName
|
||||
result.generatedModel = generatedModel
|
||||
result.derivedfrom = derivedfrom
|
||||
result.keyUid = keyUid
|
||||
result.migratedToKeycard = migratedToKeycard
|
||||
|
||||
proc `$`*(self: GeneratedWalletItem): string =
|
||||
result = fmt"""GeneratedWalletItem(
|
||||
name: {self.name},
|
||||
iconName: {self.iconName},
|
||||
generatedModel: {self.generatedModel},
|
||||
derivedfrom: {self.derivedfrom}
|
||||
derivedfrom: {self.derivedfrom},
|
||||
keyUid: {self.keyUid},
|
||||
migratedToKeycard: {self.migratedToKeycard}
|
||||
]"""
|
||||
|
||||
proc getName*(self: GeneratedWalletItem): string =
|
||||
|
@ -38,3 +46,9 @@ proc getGeneratedModel*(self: GeneratedWalletItem): Model =
|
|||
|
||||
proc getDerivedfrom*(self: GeneratedWalletItem): string =
|
||||
return self.derivedfrom
|
||||
|
||||
proc getKeyUid*(self: GeneratedWalletItem): string =
|
||||
return self.keyUid
|
||||
|
||||
proc getMigratedToKeycard*(self: GeneratedWalletItem): bool =
|
||||
return self.migratedToKeycard
|
||||
|
|
|
@ -7,7 +7,9 @@ type
|
|||
Name = UserRole + 1,
|
||||
IconName,
|
||||
GeneratedModel,
|
||||
DerivedFrom
|
||||
DerivedFrom,
|
||||
KeyUid,
|
||||
MigratedToKeycard
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -47,6 +49,8 @@ QtObject:
|
|||
ModelRole.IconName.int: "iconName",
|
||||
ModelRole.GeneratedModel.int: "generatedModel",
|
||||
ModelRole.DerivedFrom.int: "derivedfrom",
|
||||
ModelRole.KeyUid.int: "keyUid",
|
||||
ModelRole.MigratedToKeycard.int: "migratedToKeycard"
|
||||
}.toTable
|
||||
|
||||
method data(self: GeneratedWalletModel, index: QModelIndex, role: int): QVariant =
|
||||
|
@ -68,6 +72,10 @@ QtObject:
|
|||
result = newQVariant(item.getGeneratedModel())
|
||||
of ModelRole.DerivedFrom:
|
||||
result = newQVariant(item.getDerivedFrom())
|
||||
of ModelRole.KeyUid:
|
||||
result = newQVariant(item.getKeyUid())
|
||||
of ModelRole.MigratedToKeycard:
|
||||
result = newQVariant(item.getMigratedToKeycard())
|
||||
|
||||
proc setItems*(self: GeneratedWalletModel, items: seq[GeneratedWalletItem]) =
|
||||
self.beginResetModel()
|
||||
|
|
|
@ -16,6 +16,10 @@ method isLoaded*(self: AccessInterface): bool {.base.} =
|
|||
method generateNewAccount*(self: AccessInterface, password: string, accountName: string, color: string, emoji: string, path: string, derivedFrom: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addNewWalletAccountGeneratedFromKeycard*(self: AccessInterface, accountType: string, accountName: string,
|
||||
color: string, emoji: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addAccountsFromPrivateKey*(self: AccessInterface, privateKey: string, password: string, accountName: string, color: string, emoji: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
@ -53,4 +57,20 @@ method authenticateUser*(self: AccessInterface) {.base.} =
|
|||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method authenticateUserAndDeriveAddressOnKeycardForPath*(self: AccessInterface, keyUid: string, derivationPath: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method createSharedKeycardModule*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method destroySharedKeycarModule*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onUserAuthenticatedAndWalletAddressGenerated*(self: AccessInterface, address: string, publicKey: string,
|
||||
derivedFrom: string, password: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addressDetailsFetched*(self: AccessInterface, derivedAddress: DerivedAddressDto, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -17,6 +17,8 @@ type
|
|||
emoji: string
|
||||
derivedfrom: string
|
||||
relatedAccounts: compact_model.Model
|
||||
keyUid: string
|
||||
migratedToKeycard: bool
|
||||
|
||||
proc initItem*(
|
||||
name: string,
|
||||
|
@ -31,7 +33,9 @@ proc initItem*(
|
|||
assets: token_model.Model,
|
||||
emoji: string,
|
||||
derivedfrom: string,
|
||||
relatedAccounts: compact_model.Model
|
||||
relatedAccounts: compact_model.Model,
|
||||
keyUid: string,
|
||||
migratedToKeycard: bool
|
||||
): Item =
|
||||
result.name = name
|
||||
result.address = address
|
||||
|
@ -46,6 +50,8 @@ proc initItem*(
|
|||
result.emoji = emoji
|
||||
result.derivedfrom = derivedfrom
|
||||
result.relatedAccounts = relatedAccounts
|
||||
result.keyUid = keyUid
|
||||
result.migratedToKeycard = migratedToKeycard
|
||||
|
||||
proc `$`*(self: Item): string =
|
||||
result = fmt"""WalletAccountItem(
|
||||
|
@ -62,6 +68,8 @@ proc `$`*(self: Item): string =
|
|||
emoji: {self.emoji},
|
||||
derivedfrom: {self.derivedfrom},
|
||||
relatedAccounts: {self.relatedAccounts}
|
||||
keyUid: {self.keyUid},
|
||||
migratedToKeycard: {self.migratedToKeycard}
|
||||
]"""
|
||||
|
||||
proc getName*(self: Item): string =
|
||||
|
@ -102,3 +110,9 @@ proc getDerivedFrom*(self: Item): string =
|
|||
|
||||
proc getRelatedAccounts*(self: Item): compact_model.Model =
|
||||
return self.relatedAccounts
|
||||
|
||||
proc getKeyUid*(self: Item): string =
|
||||
return self.keyUid
|
||||
|
||||
proc getMigratedToKeycard*(self: Item): bool =
|
||||
return self.migratedToKeycard
|
||||
|
|
|
@ -16,7 +16,9 @@ type
|
|||
Assets,
|
||||
Emoji,
|
||||
DerivedFrom,
|
||||
RelatedAccounts
|
||||
RelatedAccounts,
|
||||
KeyUid,
|
||||
MigratedToKeycard
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -64,7 +66,9 @@ QtObject:
|
|||
ModelRole.CurrencyBalance.int:"currencyBalance",
|
||||
ModelRole.Emoji.int: "emoji",
|
||||
ModelRole.DerivedFrom.int: "derivedfrom",
|
||||
ModelRole.RelatedAccounts.int: "relatedAccounts"
|
||||
ModelRole.RelatedAccounts.int: "relatedAccounts",
|
||||
ModelRole.KeyUid.int: "keyUid",
|
||||
ModelRole.MigratedToKeycard.int: "migratedToKeycard"
|
||||
}.toTable
|
||||
|
||||
|
||||
|
@ -111,6 +115,10 @@ QtObject:
|
|||
result = newQVariant(item.getDerivedFrom())
|
||||
of ModelRole.RelatedAccounts:
|
||||
result = newQVariant(item.getRelatedAccounts())
|
||||
of ModelRole.KeyUid:
|
||||
result = newQVariant(item.getKeyUid())
|
||||
of ModelRole.MigratedToKeycard:
|
||||
result = newQVariant(item.getMigratedToKeycard())
|
||||
|
||||
proc getAccountNameByAddress*(self: Model, address: string): string =
|
||||
for account in self.items:
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
import tables, NimQml, sequtils, sugar
|
||||
import tables, NimQml, sequtils, sugar, chronicles
|
||||
|
||||
import ./io_interface, ./view, ./item, ./controller
|
||||
import ../io_interface as delegate_interface
|
||||
import ../../../../global/global_singleton
|
||||
import ../../../../core/eventemitter
|
||||
import ../../../../../app_service/common/account_constants
|
||||
import ../../../../../app_service/service/keycard/service as keycard_service
|
||||
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
|
||||
import ../../../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../shared_models/token_model as token_model
|
||||
import ../../../shared_models/token_item as token_item
|
||||
import ../../../shared_modules/keycard_popup/module as keycard_shared_module
|
||||
import ./compact_item as compact_item
|
||||
import ./compact_model as compact_model
|
||||
|
||||
export io_interface
|
||||
|
||||
type WalletAccountDetails = object
|
||||
address: string
|
||||
path: string
|
||||
addressAccountIsDerivedFrom: string
|
||||
publicKey: string
|
||||
keyUid: string
|
||||
|
||||
type
|
||||
Module* = ref object of io_interface.AccessInterface
|
||||
delegate: delegate_interface.AccessInterface
|
||||
|
@ -20,16 +30,25 @@ type
|
|||
view: View
|
||||
controller: Controller
|
||||
moduleLoaded: bool
|
||||
keycardService: keycard_service.Service
|
||||
accountsService: accounts_service.Service
|
||||
walletAccountService: wallet_account_service.Service
|
||||
keycardSharedModule: keycard_shared_module.AccessInterface
|
||||
walletAccountWhichIsAboutToBeAdded: WalletAccountDetails
|
||||
|
||||
proc newModule*(
|
||||
delegate: delegate_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
keycardService: keycard_service.Service,
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
accountsService: accounts_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.keycardService = keycardService
|
||||
result.accountsService = accountsService
|
||||
result.walletAccountService = walletAccountService
|
||||
result.view = newView(result)
|
||||
result.controller = controller.newController(result, events, walletAccountService, accountsService)
|
||||
result.moduleLoaded = false
|
||||
|
@ -37,9 +56,18 @@ proc newModule*(
|
|||
method delete*(self: Module) =
|
||||
self.view.delete
|
||||
self.controller.delete
|
||||
if not self.keycardSharedModule.isNil:
|
||||
self.keycardSharedModule.delete
|
||||
|
||||
method refreshWalletAccounts*(self: Module) =
|
||||
let keyPairMigrated = proc(migratedKeyPairs: seq[KeyPairDto], keyUid: string): bool =
|
||||
for kp in migratedKeyPairs:
|
||||
if kp.keyUid == keyUid:
|
||||
return true
|
||||
return false
|
||||
|
||||
let walletAccounts = self.controller.getWalletAccounts()
|
||||
let migratedKeyPairs = self.controller.getAllMigratedKeyPairs()
|
||||
|
||||
|
||||
let items = walletAccounts.map(proc (w: WalletAccountDto): item.Item =
|
||||
|
@ -102,7 +130,9 @@ method refreshWalletAccounts*(self: Module) =
|
|||
assets,
|
||||
w.emoji,
|
||||
w.derivedfrom,
|
||||
relatedAccounts
|
||||
relatedAccounts,
|
||||
w.keyUid,
|
||||
keyPairMigrated(migratedKeyPairs, w.keyUid)
|
||||
))
|
||||
|
||||
self.view.setItems(items)
|
||||
|
@ -136,6 +166,9 @@ method load*(self: Module) =
|
|||
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
|
||||
self.refreshWalletAccounts()
|
||||
|
||||
self.events.on(SIGNAL_NEW_KEYCARD_SET) do(e: Args):
|
||||
self.refreshWalletAccounts()
|
||||
|
||||
self.controller.init()
|
||||
self.view.load()
|
||||
|
||||
|
@ -147,14 +180,20 @@ method viewDidLoad*(self: Module) =
|
|||
self.moduleLoaded = true
|
||||
self.delegate.accountsModuleDidLoad()
|
||||
|
||||
method generateNewAccount*(self: Module, password: string, accountName: string, color: string, emoji: string, path: string, derivedFrom: string): string =
|
||||
return self.controller.generateNewAccount(password, accountName, color, emoji, path, derivedFrom)
|
||||
method generateNewAccount*(self: Module, password: string, accountName: string, color: string, emoji: string,
|
||||
path: string, derivedFrom: string): string =
|
||||
let skipPasswordVerification = singletonInstance.userProfile.getIsKeycardUser()
|
||||
return self.controller.generateNewAccount(password, accountName, color, emoji, path, derivedFrom, skipPasswordVerification)
|
||||
|
||||
method addAccountsFromPrivateKey*(self: Module, privateKey: string, password: string, accountName: string, color: string, emoji: string): string =
|
||||
return self.controller.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji)
|
||||
method addAccountsFromPrivateKey*(self: Module, privateKey: string, password: string, accountName: string, color: string,
|
||||
emoji: string): string =
|
||||
let skipPasswordVerification = singletonInstance.userProfile.getIsKeycardUser()
|
||||
return self.controller.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji, skipPasswordVerification)
|
||||
|
||||
method addAccountsFromSeed*(self: Module, seedPhrase: string, password: string, accountName: string, color: string, emoji: string, path: string): string =
|
||||
return self.controller.addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path)
|
||||
method addAccountsFromSeed*(self: Module, seedPhrase: string, password: string, accountName: string, color: string,
|
||||
emoji: string, path: string): string =
|
||||
let skipPasswordVerification = singletonInstance.userProfile.getIsKeycardUser()
|
||||
return self.controller.addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path, skipPasswordVerification)
|
||||
|
||||
method addWatchOnlyAccount*(self: Module, address: string, accountName: string, color: string, emoji: string): string =
|
||||
return self.controller.addWatchOnlyAccount(address, accountName, color, emoji)
|
||||
|
@ -186,3 +225,76 @@ method onUserAuthenticated*(self: Module, password: string) =
|
|||
self.view.userAuthenticationSuccess(password)
|
||||
else:
|
||||
self.view.userAuthentiactionFail()
|
||||
|
||||
method createSharedKeycardModule*(self: Module) =
|
||||
self.keycardSharedModule = keycard_shared_module.newModule[Module](self, UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER,
|
||||
self.events, self.keycardService, settingsService = nil, privacyService = nil, self.accountsService,
|
||||
self.walletAccountService, keychainService = nil)
|
||||
|
||||
method destroySharedKeycarModule*(self: Module) =
|
||||
self.keycardSharedModule.delete
|
||||
self.keycardSharedModule = nil
|
||||
|
||||
proc checkIfWalletAccountIsAlreadyCreated(self: Module, keyUid: string, derivationPath: string): bool =
|
||||
let walletAccounts = self.controller.getWalletAccounts()
|
||||
for w in walletAccounts:
|
||||
if w.keyUid == keyUid and w.path == derivationPath:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc findFirstAvaliablePathForWallet(self: Module, keyUid: string): string =
|
||||
let walletAccounts = self.controller.getWalletAccounts()
|
||||
# starting from 1, "0" is already reserved for the default wallet account
|
||||
for i in 1 .. 256: # we hope that nobody will have 256 accounts added, so no need to change derivation tree
|
||||
let path = account_constants.PATH_WALLET_ROOT & "/" & $i
|
||||
var found = false
|
||||
for w in walletAccounts:
|
||||
if w.keyUid == keyUid and w.path == path:
|
||||
found = true
|
||||
break
|
||||
if not found:
|
||||
return path
|
||||
error "we couldn't find available wallet account path"
|
||||
|
||||
method authenticateUserAndDeriveAddressOnKeycardForPath*(self: Module, keyUid: string, derivationPath: string) =
|
||||
var finalPath = derivationPath
|
||||
if self.checkIfWalletAccountIsAlreadyCreated(keyUid, finalPath):
|
||||
finalPath = self.findFirstAvaliablePathForWallet(keyUid)
|
||||
if self.keycardSharedModule.isNil:
|
||||
self.createSharedKeycardModule()
|
||||
self.walletAccountWhichIsAboutToBeAdded = WalletAccountDetails(keyUid: keyUid, path: finalPath)
|
||||
self.view.setDerivedAddressesLoading(true)
|
||||
self.view.setDerivedAddressesError("")
|
||||
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.AuthenticateAndDeriveAccountAddress, keyUid, finalPath)
|
||||
|
||||
method onUserAuthenticatedAndWalletAddressGenerated*(self: Module, address: string, publicKey: string,
|
||||
derivedFrom: string, password: string) =
|
||||
if address.len == 0:
|
||||
self.view.setDerivedAddressesError("wrong-path") # this should be checked on the UI side and we should not allow entering invalid path format
|
||||
if password.len == 0:
|
||||
self.view.setDerivedAddressesLoading(false)
|
||||
return
|
||||
self.onUserAuthenticated(password)
|
||||
self.walletAccountWhichIsAboutToBeAdded.address = address
|
||||
self.walletAccountWhichIsAboutToBeAdded.addressAccountIsDerivedFrom = derivedFrom
|
||||
self.walletAccountWhichIsAboutToBeAdded.publicKey = publicKey
|
||||
self.controller.fetchDerivedAddressDetails(address)
|
||||
|
||||
method addressDetailsFetched*(self: Module, derivedAddress: DerivedAddressDto, error: string) =
|
||||
var derivedAddressDto = derivedAddress
|
||||
if error.len > 0:
|
||||
derivedAddressDto.address = self.walletAccountWhichIsAboutToBeAdded.address
|
||||
derivedAddressDto.alreadyCreated = true
|
||||
self.view.setDerivedAddresses(@[derivedAddressDto], error)
|
||||
|
||||
method addNewWalletAccountGeneratedFromKeycard*(self: Module, accountType: string, accountName: string, color: string,
|
||||
emoji: string): string =
|
||||
return self.controller.addWalletAccount(accountName,
|
||||
self.walletAccountWhichIsAboutToBeAdded.address,
|
||||
self.walletAccountWhichIsAboutToBeAdded.path,
|
||||
self.walletAccountWhichIsAboutToBeAdded.addressAccountIsDerivedFrom,
|
||||
self.walletAccountWhichIsAboutToBeAdded.publicKey,
|
||||
self.walletAccountWhichIsAboutToBeAdded.keyUid,
|
||||
accountType,
|
||||
color,
|
||||
emoji)
|
|
@ -146,7 +146,7 @@ QtObject:
|
|||
proc getDerivedAddressesError(self: View): string {.slot.} =
|
||||
return self.derivedAddressesError
|
||||
|
||||
proc setDerivedAddressesError(self: View, error: string) {.slot.} =
|
||||
proc setDerivedAddressesError*(self: View, error: string) {.slot.} =
|
||||
self.derivedAddressesError = error
|
||||
self.derivedAddressErrorChanged()
|
||||
|
||||
|
@ -175,7 +175,8 @@ QtObject:
|
|||
|
||||
var generatedAccs: Model = newModel()
|
||||
generatedAccs.setItems(items.filter(x => cmpIgnoreCase(x.getDerivedFrom(), item.getDerivedFrom()) == 0))
|
||||
generatedAccounts.add(initGeneratedWalletItem("Default", "status", generatedAccs, item.getDerivedFrom()))
|
||||
generatedAccounts.add(initGeneratedWalletItem("Default", "status", generatedAccs, item.getDerivedFrom(),
|
||||
item.getKeyUid(), item.getMigratedToKeycard()))
|
||||
generated.add(item)
|
||||
|
||||
# Account generated from profile seed phrase
|
||||
|
@ -191,7 +192,8 @@ QtObject:
|
|||
var generatedAccs1: Model = newModel()
|
||||
var filterItems: seq[Item] = items.filter(x => cmpIgnoreCase(x.getDerivedFrom(), item.getDerivedFrom()) == 0)
|
||||
generatedAccs1.setItems(filterItems)
|
||||
generatedAccounts.add(initGeneratedWalletItem("Seed " & $importedSeedIndex , "seed-phrase", generatedAccs1, item.getDerivedFrom()))
|
||||
generatedAccounts.add(initGeneratedWalletItem("Seed " & $importedSeedIndex , "seed-phrase", generatedAccs1, item.getDerivedFrom(),
|
||||
item.getKeyUid(), item.getMigratedToKeycard()))
|
||||
imported.add(item)
|
||||
importedSeedIndex += 1
|
||||
|
||||
|
@ -207,6 +209,10 @@ QtObject:
|
|||
proc generateNewAccount*(self: View, password: string, accountName: string, color: string, emoji: string, path: string, derivedFrom: string): string {.slot.} =
|
||||
return self.delegate.generateNewAccount(password, accountName, color, emoji, path, derivedFrom)
|
||||
|
||||
proc addNewWalletAccountGeneratedFromKeycard*(self: View, accountType: string, accountName: string, color: string,
|
||||
emoji: string): string {.slot.} =
|
||||
return self.delegate.addNewWalletAccountGeneratedFromKeycard(accountType, accountName, color, emoji)
|
||||
|
||||
proc addAccountsFromPrivateKey*(self: View, privateKey: string, password: string, accountName: string, color: string, emoji: string): string {.slot.} =
|
||||
return self.delegate.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji)
|
||||
|
||||
|
@ -282,4 +288,13 @@ QtObject:
|
|||
proc userAuthentiactionFail*(self: View) {.signal.}
|
||||
|
||||
proc authenticateUser*(self: View) {.slot.} =
|
||||
self.delegate.authenticateUser()
|
||||
self.delegate.authenticateUser()
|
||||
|
||||
proc createSharedKeycardModule*(self: View) {.slot.} =
|
||||
self.delegate.createSharedKeycardModule()
|
||||
|
||||
proc destroySharedKeycarModule*(self: View) {.slot.} =
|
||||
self.delegate.destroySharedKeycarModule()
|
||||
|
||||
proc authenticateUserAndDeriveAddressOnKeycardForPath*(self: View, keyUid: string, derivationPath: string) {.slot.} =
|
||||
self.delegate.authenticateUserAndDeriveAddressOnKeycardForPath(keyUid, derivationPath)
|
|
@ -14,6 +14,7 @@ import ./buy_sell_crypto/module as buy_sell_crypto_module
|
|||
|
||||
import ../../../global/global_singleton
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/keycard/service as keycard_service
|
||||
import ../../../../app_service/service/token/service as token_service
|
||||
import ../../../../app_service/service/transaction/service as transaction_service
|
||||
import ../../../../app_service/service/collectible/service as collectible_service
|
||||
|
@ -53,7 +54,8 @@ proc newModule*(
|
|||
settingsService: settings_service.Service,
|
||||
savedAddressService: saved_address_service.Service,
|
||||
networkService: network_service.Service,
|
||||
accountsService: accounts_service.Service
|
||||
accountsService: accounts_service.Service,
|
||||
keycardService: keycard_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
|
@ -62,7 +64,7 @@ proc newModule*(
|
|||
result.controller = newController(result, settingsService, walletAccountService, networkService)
|
||||
result.view = newView(result)
|
||||
|
||||
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, accountsService)
|
||||
result.accountsModule = accounts_module.newModule(result, events, keycardService, walletAccountService, accountsService)
|
||||
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService)
|
||||
result.collectiblesModule = collectibles_module.newModule(result, events, collectibleService, walletAccountService)
|
||||
result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService)
|
||||
|
|
|
@ -81,6 +81,9 @@ proc newController*(delegate: io_interface.AccessInterface,
|
|||
proc serviceApplicable[T](service: T): bool =
|
||||
if not service.isNil:
|
||||
return true
|
||||
when (service is keycard_service.Service):
|
||||
error "KeycardService is mandatory for using shared keycard popup module"
|
||||
return
|
||||
var serviceName = ""
|
||||
when (service is wallet_account_service.Service):
|
||||
serviceName = "WalletAccountService"
|
||||
|
@ -88,7 +91,11 @@ proc serviceApplicable[T](service: T): bool =
|
|||
serviceName = "PrivacyService"
|
||||
when (service is settings_service.Service):
|
||||
serviceName = "SettingsService"
|
||||
debug "service doesn't meant to be used from the context it's used, check the context shared popup module is used", service=serviceName
|
||||
when (service is accounts_service.Service):
|
||||
serviceName = "AccountsService"
|
||||
when (service is keychain_service.Service):
|
||||
serviceName = "KeychainService"
|
||||
debug "service is not set, check the context shared keycard popup module is used", service=serviceName
|
||||
|
||||
proc disconnectKeycardReponseSignal(self: Controller) =
|
||||
self.events.disconnect(self.connectionKeycardResponse)
|
||||
|
@ -130,7 +137,7 @@ proc init*(self: Controller) =
|
|||
if args.uniqueIdentifier != self.uniqueIdentifier:
|
||||
return
|
||||
self.connectKeycardReponseSignal()
|
||||
self.delegate.onUserAuthenticated(args.password)
|
||||
self.delegate.onUserAuthenticated(args.password, args.pin)
|
||||
self.connectionIds.add(handlerId)
|
||||
|
||||
proc getKeycardData*(self: Controller): string =
|
||||
|
@ -259,31 +266,47 @@ proc getSeedPhraseLength*(self: Controller): int =
|
|||
return self.tmpSeedPhraseLength
|
||||
|
||||
proc validSeedPhrase*(self: Controller, seedPhrase: string): bool =
|
||||
if not serviceApplicable(self.accountsService):
|
||||
return
|
||||
let err = self.accountsService.validateMnemonic(seedPhrase)
|
||||
return err.len == 0
|
||||
|
||||
proc getKeyUidForSeedPhrase*(self: Controller, seedPhrase: string): string =
|
||||
if not serviceApplicable(self.accountsService):
|
||||
return
|
||||
let acc = self.accountsService.createAccountFromMnemonic(seedPhrase)
|
||||
return acc.keyUid
|
||||
|
||||
proc seedPhraseRefersToSelectedKeyPair*(self: Controller, seedPhrase: string): bool =
|
||||
if not serviceApplicable(self.accountsService):
|
||||
return
|
||||
let acc = self.accountsService.createAccountFromMnemonic(seedPhrase)
|
||||
return acc.keyUid == self.tmpSelectedKeyPairDto.keyUid
|
||||
|
||||
proc verifyPassword*(self: Controller, password: string): bool =
|
||||
if not serviceApplicable(self.accountsService):
|
||||
return
|
||||
return self.accountsService.verifyPassword(password)
|
||||
|
||||
proc convertSelectedKeyPairToKeycardAccount*(self: Controller, password: string): bool =
|
||||
if not serviceApplicable(self.accountsService):
|
||||
return
|
||||
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
|
||||
return self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid, password)
|
||||
|
||||
proc getLoggedInAccount*(self: Controller): AccountDto =
|
||||
if not serviceApplicable(self.accountsService):
|
||||
return
|
||||
return self.accountsService.getLoggedInAccount()
|
||||
|
||||
proc getCurrentKeycardServiceFlow*(self: Controller): keycard_service.KCSFlowType =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
return self.keycardService.getCurrentFlow()
|
||||
|
||||
proc getLastReceivedKeycardData*(self: Controller): tuple[flowType: string, flowEvent: KeycardEvent] =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
return self.keycardService.getLastReceivedKeycardData()
|
||||
|
||||
proc getMetadataFromKeycard*(self: Controller): CardMetadata =
|
||||
|
@ -297,40 +320,71 @@ proc setMetadataFromKeycard*(self: Controller, cardMetadata: CardMetadata, updat
|
|||
proc setNamePropForKeyPairStoredOnKeycard*(self: Controller, name: string) =
|
||||
self.delegate.setNamePropForKeyPairStoredOnKeycard(name)
|
||||
|
||||
proc notifyAboutGeneratedWalletAccount*(self: Controller, generatedWalletAccount: GeneratedWalletAccount, derivedFrom: string) =
|
||||
let data = SharedKeycarModuleUserAuthenticatedAndWalletAddressGeneratedArgs(uniqueIdentifier: self.uniqueIdentifier,
|
||||
address: generatedWalletAccount.address,
|
||||
publicKey: generatedWalletAccount.publicKey,
|
||||
derivedFrom: derivedFrom,
|
||||
password: self.getPassword()
|
||||
)
|
||||
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED_AND_WALLET_ADDRESS_GENERATED, data)
|
||||
|
||||
proc runSharedModuleFlow*(self: Controller, flowToRun: FlowType) =
|
||||
self.delegate.runFlow(flowToRun)
|
||||
|
||||
proc cancelCurrentFlow*(self: Controller) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.keycardService.cancelCurrentFlow()
|
||||
# in most cases we're running another flow after canceling the current one,
|
||||
# this way we're giving to the keycard some time to cancel the current flow
|
||||
sleep(200)
|
||||
|
||||
proc runGetAppInfoFlow*(self: Controller, factoryReset = false) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.cancelCurrentFlow()
|
||||
self.keycardService.startGetAppInfoFlow(factoryReset)
|
||||
|
||||
proc runGetMetadataFlow*(self: Controller, resolveAddress = false) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.cancelCurrentFlow()
|
||||
self.keycardService.startGetMetadataFlow(resolveAddress)
|
||||
|
||||
proc runChangePinFlow*(self: Controller) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.cancelCurrentFlow()
|
||||
self.keycardService.startChangePinFlow()
|
||||
|
||||
proc runChangePukFlow*(self: Controller) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.cancelCurrentFlow()
|
||||
self.keycardService.startChangePukFlow()
|
||||
|
||||
proc runChangePairingFlow*(self: Controller) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.cancelCurrentFlow()
|
||||
self.keycardService.startChangePairingFlow()
|
||||
|
||||
proc runStoreMetadataFlow*(self: Controller, cardName: string, pin: string, walletPaths: seq[string]) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.cancelCurrentFlow()
|
||||
self.keycardService.startStoreMetadataFlow(cardName, pin, walletPaths)
|
||||
|
||||
proc runDeriveAccountFlow*(self: Controller, bip44Path = "", pin = "") =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.cancelCurrentFlow()
|
||||
self.keycardService.startExportPublicFlow(bip44Path, exportMasterAddr=true, exportPrivateAddr=false, pin)
|
||||
|
||||
proc runLoadAccountFlow*(self: Controller, seedPhraseLength = 0, seedPhrase = "", puk = "", factoryReset = false) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.cancelCurrentFlow()
|
||||
self.keycardService.startLoadAccountFlow(seedPhraseLength, seedPhrase, puk, factoryReset)
|
||||
|
||||
|
@ -338,6 +392,8 @@ proc runSignFlow*(self: Controller, keyUid = "", bip44Path = "", txHash = "") =
|
|||
## For signing a transaction we need to provide a key uid of a keypair that an account we want to sign a transaction
|
||||
## for belongs to. If we're just doing an authentication for a logged in user, then default key uid is always the key
|
||||
## uid of the logged in user.
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.tmpKeyUidWhichIsBeingAuthenticating = keyUid
|
||||
if self.tmpKeyUidWhichIsBeingAuthenticating.len == 0:
|
||||
self.tmpKeyUidWhichIsBeingAuthenticating = singletonInstance.userProfile.getKeyUid()
|
||||
|
@ -345,6 +401,8 @@ proc runSignFlow*(self: Controller, keyUid = "", bip44Path = "", txHash = "") =
|
|||
self.keycardService.startSignFlow(bip44Path, txHash)
|
||||
|
||||
proc resumeCurrentFlowLater*(self: Controller) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.keycardService.resumeCurrentFlowLater()
|
||||
|
||||
proc readyToDisplayPopup*(self: Controller) =
|
||||
|
@ -357,16 +415,18 @@ proc terminateCurrentFlow*(self: Controller, lastStepInTheCurrentFlow: bool) =
|
|||
var data = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier,
|
||||
lastStepInTheCurrentFlow: lastStepInTheCurrentFlow)
|
||||
if lastStepInTheCurrentFlow:
|
||||
data.password = self.tmpPassword
|
||||
data.password = self.getPassword()
|
||||
data.pin = self.getPin()
|
||||
data.keyUid = flowEvent.keyUid
|
||||
data.txR = flowEvent.txSignature.r
|
||||
data.txS = flowEvent.txSignature.s
|
||||
data.txV = flowEvent.txSignature.v
|
||||
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED, data)
|
||||
|
||||
proc authenticateUser*(self: Controller) =
|
||||
proc authenticateUser*(self: Controller, keyUid = "") =
|
||||
self.disconnectKeycardReponseSignal()
|
||||
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: self.uniqueIdentifier)
|
||||
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: self.uniqueIdentifier,
|
||||
keyUid: keyUid)
|
||||
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
|
||||
|
||||
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
|
||||
|
@ -429,24 +489,38 @@ proc getSigningPhrase*(self: Controller): string =
|
|||
return self.settingsService.getSigningPhrase()
|
||||
|
||||
proc enterKeycardPin*(self: Controller, pin: string) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.keycardService.enterPin(pin)
|
||||
|
||||
proc enterKeycardPuk*(self: Controller, puk: string) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.keycardService.enterPuk(puk)
|
||||
|
||||
proc storePinToKeycard*(self: Controller, pin: string, puk: string) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.keycardService.storePin(pin, puk)
|
||||
|
||||
proc storePukToKeycard*(self: Controller, puk: string) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.keycardService.storePuk(puk)
|
||||
|
||||
proc storePairingCodeToKeycard*(self: Controller, pairingCode: string) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.keycardService.storePairingCode(pairingCode)
|
||||
|
||||
proc storeSeedPhraseToKeycard*(self: Controller, seedPhraseLength: int, seedPhrase: string) =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
self.keycardService.storeSeedPhrase(seedPhraseLength, seedPhrase)
|
||||
|
||||
proc generateRandomPUK*(self: Controller): string =
|
||||
if not serviceApplicable(self.keycardService):
|
||||
return
|
||||
return self.keycardService.generateRandomPUK()
|
||||
|
||||
proc isMnemonicBackedUp*(self: Controller): bool =
|
||||
|
@ -470,11 +544,15 @@ proc getMnemonicWordAtIndex*(self: Controller, index: int): string =
|
|||
return self.privacyService.getMnemonicWordAtIndex(index)
|
||||
|
||||
proc tryToObtainDataFromKeychain*(self: Controller) =
|
||||
if not serviceApplicable(self.keychainService):
|
||||
return
|
||||
if(not singletonInstance.userProfile.getUsingBiometricLogin()):
|
||||
return
|
||||
let loggedInAccount = self.getLoggedInAccount()
|
||||
self.keychainService.tryToObtainData(loggedInAccount.name)
|
||||
|
||||
proc tryToStoreDataToKeychain*(self: Controller, password: string) =
|
||||
if not serviceApplicable(self.keychainService):
|
||||
return
|
||||
let loggedInAccount = self.getLoggedInAccount()
|
||||
self.keychainService.storeData(loggedInAccount.name, password)
|
|
@ -3,6 +3,8 @@ import ../../../../app/core/eventemitter
|
|||
from ../../../../app_service/service/keycard/service import KeycardEvent, CardMetadata, KeyDetails
|
||||
import models/key_pair_item
|
||||
|
||||
const SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED_AND_WALLET_ADDRESS_GENERATED* = "sharedKeycarModuleUserAuthenticatedAndWalletAddressGenerated"
|
||||
|
||||
const SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP* = "sharedKeycarModuleDisplayPopup"
|
||||
const SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED* = "sharedKeycarModuleFlowTerminated"
|
||||
const SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER* = "sharedKeycarModuleAuthenticateUser"
|
||||
|
@ -35,6 +37,7 @@ type
|
|||
type
|
||||
SharedKeycarModuleArgs* = ref object of SharedKeycarModuleBaseArgs
|
||||
password*: string
|
||||
pin*: string # this is used in case we need to run another keycard flow which requires pin, after we successfully authenticated logged in user
|
||||
keyUid*: string
|
||||
txR*: string
|
||||
txS*: string
|
||||
|
@ -61,6 +64,15 @@ type FlowType* {.pure.} = enum
|
|||
ChangeKeycardPin = "ChangeKeycardPin"
|
||||
ChangeKeycardPuk = "ChangeKeycardPuk"
|
||||
ChangePairingCode = "ChangePairingCode"
|
||||
AuthenticateAndDeriveAccountAddress = "AuthenticateAndDeriveAccountAddress"
|
||||
|
||||
type
|
||||
SharedKeycarModuleUserAuthenticatedAndWalletAddressGeneratedArgs* = ref object of Args
|
||||
uniqueIdentifier*: string
|
||||
address*: string
|
||||
publicKey*: string
|
||||
derivedFrom*: string
|
||||
password*: string
|
||||
|
||||
type
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
|
@ -146,7 +158,7 @@ method migratingProfileKeyPair*(self: AccessInterface): bool {.base.} =
|
|||
method getSigningPhrase*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
|
||||
method onUserAuthenticated*(self: AccessInterface, password: string, pin: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method keychainObtainedDataFailure*(self: AccessInterface, errorDescription: string, errorType: string) {.base.} =
|
||||
|
|
|
@ -19,6 +19,12 @@ export io_interface
|
|||
logScope:
|
||||
topics = "keycard-popup-module"
|
||||
|
||||
type DerivingAccountDetails = object
|
||||
keyUid: string
|
||||
path: string
|
||||
deriveAddressAfterAuthentication: bool
|
||||
addressRequested: bool
|
||||
|
||||
type
|
||||
Module*[T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface
|
||||
delegate: T
|
||||
|
@ -28,6 +34,7 @@ type
|
|||
initialized: bool
|
||||
tmpLocalState: State # used when flow is run, until response arrives to determine next state appropriatelly
|
||||
authenticationPopupIsAlreadyRunning: bool
|
||||
derivingAccountDetails: DerivingAccountDetails
|
||||
|
||||
proc newModule*[T](delegate: T,
|
||||
uniqueIdentifier: string,
|
||||
|
@ -47,6 +54,8 @@ proc newModule*[T](delegate: T,
|
|||
privacyService, accountsService, walletAccountService, keychainService)
|
||||
result.initialized = false
|
||||
result.authenticationPopupIsAlreadyRunning = false
|
||||
result.derivingAccountDetails.deriveAddressAfterAuthentication = false
|
||||
result.derivingAccountDetails.addressRequested = false
|
||||
|
||||
## Forward declaration
|
||||
proc updateKeyPairItemIfDataAreKnown[T](self: Module[T], address: string, item: var KeyPairItem): bool
|
||||
|
@ -207,6 +216,13 @@ method onTertiaryActionClicked*[T](self: Module[T]) =
|
|||
debug "sm_tertiary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
|
||||
|
||||
method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEvent: KeycardEvent) =
|
||||
if self.derivingAccountDetails.deriveAddressAfterAuthentication and
|
||||
self.derivingAccountDetails.addressRequested:
|
||||
# clearing...
|
||||
self.derivingAccountDetails = DerivingAccountDetails()
|
||||
# notify about generated address
|
||||
self.controller.notifyAboutGeneratedWalletAccount(keycardEvent.generatedWalletAccount, keycardEvent.masterKeyAddress)
|
||||
return
|
||||
## Check local state first, in case postponed flow is run
|
||||
if not self.tmpLocalState.isNil:
|
||||
let nextState = self.tmpLocalState.resolveKeycardNextState(keycardFlowType, keycardEvent, self.controller)
|
||||
|
@ -239,14 +255,6 @@ proc buildKeyPairsList[T](self: Module[T], excludeAlreadyMigratedPairs: bool): s
|
|||
if kp.keyUid == keyUid:
|
||||
return true
|
||||
|
||||
let findItemByDerivedFromAddress = proc(items: seq[KeyPairItem], address: string): KeyPairItem =
|
||||
if address.len == 0:
|
||||
return nil
|
||||
for i in 0 ..< items.len:
|
||||
if(items[i].derivedFrom == address):
|
||||
return items[i]
|
||||
return nil
|
||||
|
||||
let countOfKeyPairsForType = proc(items: seq[KeyPairItem], keyPairType: KeyPairType): int =
|
||||
result = 0
|
||||
for i in 0 ..< items.len:
|
||||
|
@ -258,50 +266,52 @@ proc buildKeyPairsList[T](self: Module[T], excludeAlreadyMigratedPairs: bool): s
|
|||
for a in accounts:
|
||||
if a.isChat or a.walletType == WalletTypeWatch or (excludeAlreadyMigratedPairs and keyPairMigrated(a.keyUid)):
|
||||
continue
|
||||
var item = findItemByDerivedFromAddress(items, a.derivedfrom)
|
||||
if a.walletType == WalletTypeDefaultStatusAccount or a.walletType == WalletTypeGenerated:
|
||||
if item.isNil:
|
||||
item = initKeyPairItem(pubKey = a.publicKey,
|
||||
keyUid = a.keyUid,
|
||||
locked = false,
|
||||
name = singletonInstance.userProfile.getName(),
|
||||
image = singletonInstance.userProfile.getIcon(),
|
||||
icon = "",
|
||||
pairType = KeyPairType.Profile,
|
||||
derivedFrom = a.derivedfrom)
|
||||
items.insert(item, 0) # Status Account must be at first place
|
||||
var icon = ""
|
||||
if a.walletType == WalletTypeDefaultStatusAccount:
|
||||
icon = "wallet"
|
||||
items[0].addAccount(a.name, a.path, a.address, a.emoji, a.color, icon, balance = 0.0)
|
||||
if a.walletType == WalletTypeDefaultStatusAccount:
|
||||
var item = initKeyPairItem(pubKey = a.publicKey,
|
||||
keyUid = a.keyUid,
|
||||
locked = false,
|
||||
name = singletonInstance.userProfile.getName(),
|
||||
image = singletonInstance.userProfile.getIcon(),
|
||||
icon = "wallet",
|
||||
pairType = KeyPairType.Profile,
|
||||
derivedFrom = a.derivedfrom)
|
||||
for ga in accounts:
|
||||
if cmpIgnoreCase(ga.derivedfrom, a.derivedfrom) != 0:
|
||||
continue
|
||||
var icon = ""
|
||||
if a.walletType == WalletTypeDefaultStatusAccount:
|
||||
icon = "wallet"
|
||||
item.addAccount(ga.name, ga.path, ga.address, ga.emoji, ga.color, icon, balance = 0.0)
|
||||
items.insert(item, 0) # Status Account must be at first place
|
||||
continue
|
||||
if a.walletType == WalletTypeSeed:
|
||||
let diffImports = countOfKeyPairsForType(items, KeyPairType.SeedImport)
|
||||
if item.isNil:
|
||||
item = initKeyPairItem(pubKey = a.publicKey,
|
||||
keyUid = a.keyUid,
|
||||
locked = false,
|
||||
name = "Seed Phrase " & $(diffImports + 1), # string created here should be transalted, but so far it's like it is
|
||||
image = "",
|
||||
icon = "key_pair_seed_phrase",
|
||||
pairType = KeyPairType.SeedImport,
|
||||
derivedFrom = a.derivedfrom)
|
||||
items.add(item)
|
||||
item.addAccount(a.name, a.path, a.address, a.emoji, a.color, icon = "", balance = 0.0)
|
||||
var item = initKeyPairItem(pubKey = a.publicKey,
|
||||
keyUid = a.keyUid,
|
||||
locked = false,
|
||||
name = "Seed Phrase " & $(diffImports + 1), # string created here should be transalted, but so far it's like it is
|
||||
image = "",
|
||||
icon = "key_pair_seed_phrase",
|
||||
pairType = KeyPairType.SeedImport,
|
||||
derivedFrom = a.derivedfrom)
|
||||
for ga in accounts:
|
||||
if cmpIgnoreCase(ga.derivedfrom, a.derivedfrom) != 0:
|
||||
continue
|
||||
item.addAccount(ga.name, ga.path, ga.address, ga.emoji, ga.color, icon = "", balance = 0.0)
|
||||
items.add(item)
|
||||
continue
|
||||
if a.walletType == WalletTypeKey:
|
||||
let diffImports = countOfKeyPairsForType(items, KeyPairType.PrivateKeyImport)
|
||||
if item.isNil:
|
||||
item = initKeyPairItem(pubKey = a.publicKey,
|
||||
keyUid = a.keyUid,
|
||||
locked = false,
|
||||
name = "Key " & $(diffImports + 1), # string created here should be transalted, but so far it's like it is
|
||||
image = "",
|
||||
icon = "key_pair_private_key",
|
||||
pairType = KeyPairType.PrivateKeyImport,
|
||||
derivedFrom = a.derivedfrom)
|
||||
items.add(item)
|
||||
var item = initKeyPairItem(pubKey = a.publicKey,
|
||||
keyUid = a.keyUid,
|
||||
locked = false,
|
||||
name = "Key " & $(diffImports + 1), # string created here should be transalted, but so far it's like it is
|
||||
image = "",
|
||||
icon = "key_pair_private_key",
|
||||
pairType = KeyPairType.PrivateKeyImport,
|
||||
derivedFrom = a.derivedfrom)
|
||||
item.addAccount(a.name, a.path, a.address, a.emoji, a.color, icon = "", balance = 0.0)
|
||||
items.add(item)
|
||||
continue
|
||||
if items.len == 0:
|
||||
debug "sm_there is no any key pair for the logged in user that is not already migrated to a keycard"
|
||||
|
@ -409,6 +419,15 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Path
|
|||
self.tmpLocalState = newReadingKeycardState(flowToRun, nil)
|
||||
self.controller.runChangePairingFlow()
|
||||
return
|
||||
if flowToRun == FlowType.AuthenticateAndDeriveAccountAddress:
|
||||
self.derivingAccountDetails = DerivingAccountDetails(
|
||||
keyUid: keyUid,
|
||||
path: bip44Path,
|
||||
deriveAddressAfterAuthentication: true,
|
||||
addressRequested: false
|
||||
)
|
||||
self.controller.authenticateUser(keyUid)
|
||||
return
|
||||
|
||||
method setSelectedKeyPair*[T](self: Module[T], item: KeyPairItem) =
|
||||
var paths: seq[string]
|
||||
|
@ -463,7 +482,12 @@ method setKeyPairStoredOnKeycard*[T](self: Module[T], cardMetadata: CardMetadata
|
|||
method setNamePropForKeyPairStoredOnKeycard*[T](self: Module[T], name: string) =
|
||||
self.view.setNamePropForKeyPairStoredOnKeycard(name)
|
||||
|
||||
method onUserAuthenticated*[T](self: Module[T], password: string) =
|
||||
method onUserAuthenticated*[T](self: Module[T], password: string, pin: string) =
|
||||
if self.derivingAccountDetails.deriveAddressAfterAuthentication:
|
||||
self.derivingAccountDetails.addressRequested = true
|
||||
self.controller.setPassword(password)
|
||||
self.controller.runDeriveAccountFlow(self.derivingAccountDetails.path, pin)
|
||||
return
|
||||
let currStateObj = self.view.currentStateObj()
|
||||
if not currStateObj.isNil and currStateObj.flowType() == FlowType.SetupNewKeycard:
|
||||
self.controller.setPassword(password)
|
||||
|
|
|
@ -333,6 +333,7 @@ proc setupKeycardAccount*(self: Controller, storeToKeychain: bool) =
|
|||
self.storeKeycardAccountAndLogin(storeToKeychain)
|
||||
else:
|
||||
self.delegate.moveToLoadingAppState()
|
||||
self.delegate.storeKeyPairForNewKeycardUser()
|
||||
self.accountsService.setupAccountKeycard(self.tmpKeycardEvent)
|
||||
if storeToKeychain:
|
||||
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_STORE)
|
||||
|
|
|
@ -35,7 +35,8 @@ const RequestParamNewPIN* = "new-pin"
|
|||
const RequestParamPUK* = "puk"
|
||||
const RequestParamNewPUK* = "new-puk"
|
||||
const RequestParamMasterKey* = "master-key"
|
||||
const RequestParamWalleRootKey* = "wallet-root-key"
|
||||
const RequestParamMasterKeyAddress* = "master-key-address"
|
||||
const RequestParamWalletRootKey* = "wallet-root-key"
|
||||
const RequestParamWalletKey* = "wallet-key"
|
||||
const RequestParamEIP1581Key* = "eip1581-key"
|
||||
const RequestParamWhisperKey* = "whisper-key"
|
||||
|
@ -49,6 +50,8 @@ const RequestParamBIP44Path* = "bip44-path"
|
|||
const RequestParamTXSignature* = "tx-signature"
|
||||
const RequestParamOverwrite* = "overwrite"
|
||||
const RequestParamResolveAddr* = "resolve-addresses"
|
||||
const RequestParamExportMasterAddress* = "export-master-address"
|
||||
const RequestParamExportPrivate* = "export-private"
|
||||
const RequestParamCardMeta* = "card-metadata"
|
||||
const RequestParamCardName* = "card-name"
|
||||
const RequestParamWalletPaths* = "wallet-paths"
|
||||
|
@ -98,7 +101,9 @@ const ResponseParamEIP1581Key* = RequestParamEIP1581Key
|
|||
const ResponseParamEncKey* = RequestParamEncKey
|
||||
const ResponseParamMasterKey* = RequestParamMasterKey
|
||||
const ResponseParamWalletKey* = RequestParamWalletKey
|
||||
const ResponseParamWalleRootKey* = RequestParamWalleRootKey
|
||||
const ResponseParamWalletRootKey* = RequestParamWalletRootKey
|
||||
const ResponseParamWhisperKey* = RequestParamWhisperKey
|
||||
const ResponseParamMnemonicIdxs* = RequestParamMnemonicIdxs
|
||||
const ResponseParamTXSignature* = RequestParamTXSignature
|
||||
const ResponseParamTXSignature* = RequestParamTXSignature
|
||||
const ResponseParamExportedKey* = RequestParamExportedKey
|
||||
const ResponseParamMasterKeyAddress* = RequestParamMasterKeyAddress
|
|
@ -1,3 +1,5 @@
|
|||
import strutils
|
||||
|
||||
type
|
||||
KeyDetails* = object
|
||||
address*: string
|
||||
|
@ -15,6 +17,11 @@ type
|
|||
path*: string
|
||||
address*: string
|
||||
|
||||
GeneratedWalletAccount* = object
|
||||
address*: string
|
||||
publicKey*: string
|
||||
privateKey*: string
|
||||
|
||||
CardMetadata* = object
|
||||
name*: string
|
||||
walletAccounts*: seq[WalletAccount]
|
||||
|
@ -34,6 +41,7 @@ type
|
|||
pinRetries*: int
|
||||
pukRetries*: int
|
||||
cardMetadata*: CardMetadata
|
||||
generatedWalletAccount*: GeneratedWalletAccount
|
||||
txSignature*: TransactionSignature
|
||||
eip1581Key*: KeyDetails
|
||||
encryptionKey*: KeyDetails
|
||||
|
@ -41,6 +49,7 @@ type
|
|||
walletKey*: KeyDetails
|
||||
walletRootKey*: KeyDetails
|
||||
whisperKey*: KeyDetails
|
||||
masterKeyAddress*: string
|
||||
|
||||
proc toKeyDetails(jsonObj: JsonNode): KeyDetails =
|
||||
discard jsonObj.getProp(ResponseParamAddress, result.address)
|
||||
|
@ -59,6 +68,13 @@ proc toWalletAccount(jsonObj: JsonNode): WalletAccount =
|
|||
discard jsonObj.getProp(ResponseParamPath, result.path)
|
||||
discard jsonObj.getProp(ResponseParamAddress, result.address)
|
||||
|
||||
proc toGeneratedWalletAccount(jsonObj: JsonNode): GeneratedWalletAccount =
|
||||
discard jsonObj.getProp(ResponseParamAddress, result.address)
|
||||
if jsonObj.getProp(ResponseParamPublicKey, result.publicKey) and not result.publicKey.startsWith("0x"):
|
||||
result.publicKey = "0x" & result.publicKey
|
||||
if jsonObj.getProp(ResponseParamPrivateKey, result.privateKey) and not result.privateKey.startsWith("0x"):
|
||||
result.privateKey = "0x" & result.privateKey
|
||||
|
||||
proc toCardMetadata(jsonObj: JsonNode): CardMetadata =
|
||||
discard jsonObj.getProp(ResponseParamName, result.name)
|
||||
var accountsArr: JsonNode
|
||||
|
@ -77,7 +93,8 @@ proc toKeycardEvent(jsonObj: JsonNode): KeycardEvent =
|
|||
discard jsonObj.getProp(ResponseParamFreeSlots, result.freePairingSlots)
|
||||
discard jsonObj.getProp(ResponseParamPINRetries, result.pinRetries)
|
||||
discard jsonObj.getProp(ResponseParamPUKRetries, result.pukRetries)
|
||||
if jsonObj.getProp(ResponseParamKeyUID, result.keyUid):
|
||||
discard jsonObj.getProp(ResponseParamMasterKeyAddress, result.masterKeyAddress)
|
||||
if jsonObj.getProp(ResponseParamKeyUID, result.keyUid) and not result.keyUid.startsWith("0x"):
|
||||
result.keyUid = "0x" & result.keyUid
|
||||
|
||||
var obj: JsonNode
|
||||
|
@ -96,7 +113,7 @@ proc toKeycardEvent(jsonObj: JsonNode): KeycardEvent =
|
|||
if(jsonObj.getProp(ResponseParamWalletKey, obj)):
|
||||
result.walletKey = toKeyDetails(obj)
|
||||
|
||||
if(jsonObj.getProp(ResponseParamWalleRootKey, obj)):
|
||||
if(jsonObj.getProp(ResponseParamWalletRootKey, obj)):
|
||||
result.walletRootKey = toKeyDetails(obj)
|
||||
|
||||
if(jsonObj.getProp(ResponseParamWhisperKey, obj)):
|
||||
|
@ -109,6 +126,9 @@ proc toKeycardEvent(jsonObj: JsonNode): KeycardEvent =
|
|||
|
||||
if(jsonObj.getProp(ResponseParamCardMeta, obj)):
|
||||
result.cardMetadata = toCardMetadata(obj)
|
||||
|
||||
if(jsonObj.getProp(ResponseParamExportedKey, obj)):
|
||||
result.generatedWalletAccount = toGeneratedWalletAccount(obj)
|
||||
|
||||
if(jsonObj.getProp(ResponseParamTXSignature, obj)):
|
||||
result.txSignature = toTransactionSignature(obj)
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, json, os, chronicles, random
|
||||
import NimQml, json, os, chronicles, random, strutils
|
||||
import keycard_go
|
||||
import ../../../app/core/eventemitter
|
||||
import ../../../app/core/tasks/[qt, threadpool]
|
||||
|
@ -25,6 +25,7 @@ type KCSFlowType* {.pure.} = enum
|
|||
|
||||
const EmptyTxHash = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
const DefaultBIP44Path = "m/0"
|
||||
const DefaultEIP1581Path = "m/43'/60'/1581'"
|
||||
|
||||
const PINLengthForStatusApp* = 6
|
||||
const PUKLengthForStatusApp* = 12
|
||||
|
@ -237,6 +238,23 @@ QtObject:
|
|||
self.currentFlow = KCSFlowType.ChangePairing
|
||||
self.startFlow(payload)
|
||||
|
||||
proc startExportPublicFlow*(self: Service, path: string, exportMasterAddr = false, exportPrivateAddr = false, pin = "") =
|
||||
if exportPrivateAddr and not path.startsWith(DefaultEIP1581Path):
|
||||
error "in order to export private address path must not be outside of eip1581 tree"
|
||||
return
|
||||
|
||||
var payload = %* {
|
||||
RequestParamBIP44Path: DefaultBIP44Path,
|
||||
RequestParamExportMasterAddress: exportMasterAddr,
|
||||
RequestParamExportPrivate: exportPrivateAddr
|
||||
}
|
||||
if path.len > 0:
|
||||
payload[RequestParamBIP44Path] = %* path
|
||||
if pin.len > 0:
|
||||
payload[RequestParamPIN] = %* pin
|
||||
self.currentFlow = KCSFlowType.ExportPublic
|
||||
self.startFlow(payload)
|
||||
|
||||
proc startStoreMetadataFlow*(self: Service, cardName: string, pin: string, walletPaths: seq[string]) =
|
||||
var name = cardName
|
||||
if cardName.len > CardNameLength:
|
||||
|
|
|
@ -72,6 +72,24 @@ const getDerivedAddressForPrivateKeyTask*: Task = proc(argEncoded: string) {.gcs
|
|||
}
|
||||
arg.finish(output)
|
||||
|
||||
type
|
||||
FetchDerivedAddressDetailsTaskArg* = ref object of QObjectTaskArg
|
||||
address: string
|
||||
|
||||
const fetchDerivedAddressDetailsTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[FetchDerivedAddressDetailsTaskArg](argEncoded)
|
||||
var data = %* {
|
||||
"details": "",
|
||||
"error": ""
|
||||
}
|
||||
try:
|
||||
let response = status_go_accounts.getDerivedAddressDetails(arg.address)
|
||||
data["details"] = response.result
|
||||
except Exception as e:
|
||||
let err = fmt"Error getting details for an address: {e.msg}"
|
||||
data["error"] = %* err
|
||||
arg.finish(data)
|
||||
|
||||
#################################################
|
||||
# Async timer
|
||||
#################################################
|
||||
|
|
|
@ -32,6 +32,7 @@ const SIGNAL_WALLET_ACCOUNT_UPDATED* = "walletAccount/walletAccountUpdated"
|
|||
const SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED* = "walletAccount/networkEnabledUpdated"
|
||||
const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_READY* = "walletAccount/derivedAddressesReady"
|
||||
const SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT* = "walletAccount/tokensRebuilt"
|
||||
const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_DETAILS_FETCHED* = "walletAccount/derivedAddressDetailsFetched"
|
||||
|
||||
const SIGNAL_NEW_KEYCARD_SET* = "newKeycardSet"
|
||||
const SIGNAL_KEYCARD_LOCKED* = "keycardLocked"
|
||||
|
@ -232,43 +233,73 @@ QtObject:
|
|||
self.buildAllTokens()
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_SAVED, AccountSaved(account: newAccount))
|
||||
|
||||
proc generateNewAccount*(self: Service, password: string, accountName: string, color: string, emoji: string, path: string, derivedFrom: string): string =
|
||||
proc generateNewAccount*(self: Service, password: string, accountName: string, color: string, emoji: string,
|
||||
path: string, derivedFrom: string, skipPasswordVerification: bool): string =
|
||||
try:
|
||||
discard backend.generateAccountWithDerivedPath(
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji,
|
||||
path,
|
||||
derivedFrom)
|
||||
if skipPasswordVerification:
|
||||
discard backend.generateAccountWithDerivedPathPasswordVerified(
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji,
|
||||
path,
|
||||
derivedFrom)
|
||||
else:
|
||||
discard backend.generateAccountWithDerivedPath(
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji,
|
||||
path,
|
||||
derivedFrom)
|
||||
except Exception as e:
|
||||
return fmt"Error generating new account: {e.msg}"
|
||||
|
||||
self.addNewAccountToLocalStore()
|
||||
|
||||
proc addAccountsFromPrivateKey*(self: Service, privateKey: string, password: string, accountName: string, color: string, emoji: string): string =
|
||||
proc addAccountsFromPrivateKey*(self: Service, privateKey: string, password: string, accountName: string, color: string,
|
||||
emoji: string, skipPasswordVerification: bool): string =
|
||||
try:
|
||||
discard backend.addAccountWithPrivateKey(
|
||||
privateKey,
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji)
|
||||
if skipPasswordVerification:
|
||||
discard backend.addAccountWithPrivateKeyPasswordVerified(
|
||||
privateKey,
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji)
|
||||
else:
|
||||
discard backend.addAccountWithPrivateKey(
|
||||
privateKey,
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji)
|
||||
except Exception as e:
|
||||
return fmt"Error adding account with private key: {e.msg}"
|
||||
|
||||
self.addNewAccountToLocalStore()
|
||||
|
||||
proc addAccountsFromSeed*(self: Service, mnemonic: string, password: string, accountName: string, color: string, emoji: string, path: string): string =
|
||||
proc addAccountsFromSeed*(self: Service, mnemonic: string, password: string, accountName: string, color: string,
|
||||
emoji: string, path: string, skipPasswordVerification: bool): string =
|
||||
try:
|
||||
discard backend.addAccountWithMnemonicAndPath(
|
||||
mnemonic,
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji,
|
||||
path
|
||||
)
|
||||
if skipPasswordVerification:
|
||||
discard backend.addAccountWithMnemonicAndPathPasswordVerified(
|
||||
mnemonic,
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji,
|
||||
path
|
||||
)
|
||||
else:
|
||||
discard backend.addAccountWithMnemonicAndPath(
|
||||
mnemonic,
|
||||
hashPassword(password),
|
||||
accountName,
|
||||
color,
|
||||
emoji,
|
||||
path
|
||||
)
|
||||
except Exception as e:
|
||||
return fmt"Error adding account with mnemonic: {e.msg}"
|
||||
|
||||
|
@ -379,6 +410,27 @@ QtObject:
|
|||
error: error
|
||||
))
|
||||
|
||||
proc fetchDerivedAddressDetails*(self: Service, address: string) =
|
||||
let arg = FetchDerivedAddressDetailsTaskArg(
|
||||
address: address,
|
||||
tptr: cast[ByteAddress](fetchDerivedAddressDetailsTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onDerivedAddressDetailsFetched",
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc onDerivedAddressDetailsFetched*(self: Service, jsonString: string) {.slot.} =
|
||||
var data = DerivedAddressesArgs()
|
||||
try:
|
||||
let response = parseJson(jsonString)
|
||||
let addrDto = response{"details"}.toDerivedAddressDto()
|
||||
data.derivedAddresses.add(addrDto)
|
||||
data.error = response["error"].getStr()
|
||||
except Exception as e:
|
||||
error "error: ", procName="getDerivedAddressDetails", errName = e.name, errDesription = e.msg
|
||||
data.error = e.msg
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_DETAILS_FETCHED, data)
|
||||
|
||||
proc onStartBuildingTokensTimer*(self: Service, response: string) {.slot.} =
|
||||
if ((now().toTime().toUnix() - self.timerStartTimeInSeconds) < CheckBalanceSlotExecuteIntervalInSeconds):
|
||||
self.startBuildingTokensTimer(resetTimeToNow = false)
|
||||
|
@ -566,4 +618,16 @@ QtObject:
|
|||
return self.responseHasNoErrors("deleteKeycard", response)
|
||||
except Exception as e:
|
||||
error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg
|
||||
return false
|
||||
return false
|
||||
|
||||
proc addWalletAccount*(self: Service, name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid, accountType,
|
||||
color, emoji: string): string =
|
||||
try:
|
||||
let response = status_go_accounts.saveAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid,
|
||||
accountType, color, emoji, walletDefaultAccount = false, chatDefaultAccount = false)
|
||||
if not response.error.isNil:
|
||||
return "(" & $response.error.code & ") " & response.error.message
|
||||
self.addNewAccountToLocalStore()
|
||||
except Exception as e:
|
||||
error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg
|
||||
return "error: " & e.msg
|
||||
|
|
|
@ -24,6 +24,26 @@ proc getAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
|||
proc deleteAccount*(address: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
return core.callPrivateRPC("accounts_deleteAccount", %* [address])
|
||||
|
||||
proc saveAccount*(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid, accountType, color, emoji: string,
|
||||
walletDefaultAccount: bool, chatDefaultAccount: bool):
|
||||
RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [
|
||||
[{
|
||||
"name": name,
|
||||
"address": address,
|
||||
"path": path,
|
||||
"derived-from": addressAccountIsDerivedFrom,
|
||||
"public-key": publicKey,
|
||||
"key-uid": keyUid,
|
||||
"type": accountType,
|
||||
"color": color,
|
||||
"emoji": emoji,
|
||||
"wallet": walletDefaultAccount,
|
||||
"chat": chatDefaultAccount
|
||||
}]
|
||||
]
|
||||
return core.callPrivateRPC("accounts_saveAccounts", payload)
|
||||
|
||||
proc updateAccount*(name, address, publicKey, walletType, color, emoji: string) {.raises: [Exception].} =
|
||||
discard core.callPrivateRPC("accounts_saveAccounts", %* [
|
||||
[{
|
||||
|
@ -285,6 +305,10 @@ proc getDerivedAddressForPrivateKey*(privateKey: string,): RpcResponse[JsonNode]
|
|||
let payload = %* [privateKey]
|
||||
result = core.callPrivateRPC("wallet_getDerivedAddressForPrivateKey", payload)
|
||||
|
||||
proc getDerivedAddressDetails*(address: string,): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [address]
|
||||
result = core.callPrivateRPC("wallet_getDerivedAddressDetails", payload)
|
||||
|
||||
proc verifyPassword*(password: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [password]
|
||||
return core.callPrivateRPC("accounts_verifyPassword", payload)
|
|
@ -122,6 +122,14 @@ rpc(generateAccountWithDerivedPath, "accounts"):
|
|||
path: string
|
||||
derivedFrom: string
|
||||
|
||||
rpc(generateAccountWithDerivedPathPasswordVerified, "accounts"):
|
||||
password: string
|
||||
name: string
|
||||
color: string
|
||||
emoji: string
|
||||
path: string
|
||||
derivedFrom: string
|
||||
|
||||
rpc(addAccountWithMnemonicAndPath, "accounts"):
|
||||
mnemonic: string
|
||||
password: string
|
||||
|
@ -130,6 +138,14 @@ rpc(addAccountWithMnemonicAndPath, "accounts"):
|
|||
emoji: string
|
||||
path: string
|
||||
|
||||
rpc(addAccountWithMnemonicAndPathPasswordVerified, "accounts"):
|
||||
mnemonic: string
|
||||
password: string
|
||||
name: string
|
||||
color: string
|
||||
emoji: string
|
||||
path: string
|
||||
|
||||
rpc(addAccountWithPrivateKey, "accounts"):
|
||||
privateKey: string
|
||||
password: string
|
||||
|
@ -137,6 +153,13 @@ rpc(addAccountWithPrivateKey, "accounts"):
|
|||
color: string
|
||||
emoji: string
|
||||
|
||||
rpc(addAccountWithPrivateKeyPasswordVerified, "accounts"):
|
||||
privateKey: string
|
||||
password: string
|
||||
name: string
|
||||
color: string
|
||||
emoji: string
|
||||
|
||||
rpc(addAccountWatch, "accounts"):
|
||||
address: string
|
||||
name: string
|
||||
|
|
|
@ -149,8 +149,7 @@ Item {
|
|||
//TODO remove when sync code is implemented
|
||||
opacity: 0.0
|
||||
}
|
||||
// TODO: Functionality is not completed. Missing saving of keystore files which blocks wallet account creation. @see #7867
|
||||
PropertyChanges {
|
||||
PropertyChanges {
|
||||
target: keycardLink
|
||||
text: qsTr("Login with Keycard")
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import StatusQ.Core 0.1
|
|||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
|
@ -14,9 +15,17 @@ import "../stores"
|
|||
Item {
|
||||
id: derivedAddresses
|
||||
|
||||
property int selectedAccountType: RootStore.defaultSelectedType
|
||||
property string selectedKeyUid: RootStore.defaultSelectedKeyUid
|
||||
property bool selectedKeyUidMigratedToKeycard: RootStore.defaultSelectedKeyUidMigratedToKeycard
|
||||
property string selectedPath: ""
|
||||
property string pathSubFix: ""
|
||||
property bool isLoading: RootStore.derivedAddressesLoading
|
||||
property bool pathError: Utils.isInvalidPath(RootStore.derivedAddressesError)
|
||||
property string enterPasswordIcon: ""
|
||||
|
||||
property alias selectedAddress: selectedDerivedAddress.title
|
||||
property alias selectedAddressAvailable: selectedDerivedAddress.enabled
|
||||
|
||||
function reset() {
|
||||
RootStore.resetDerivedAddressModel()
|
||||
|
@ -42,6 +51,10 @@ Item {
|
|||
|
||||
QtObject {
|
||||
id: _internal
|
||||
|
||||
readonly property bool showEnterPinPassButton: !RootStore.loggedInUserAuthenticated &&
|
||||
derivedAddresses.selectedAccountType !== SelectGeneratedAccount.AddAccountType.ImportSeedPhrase &&
|
||||
derivedAddresses.selectedAccountType !== SelectGeneratedAccount.AddAccountType.ImportPrivateKey
|
||||
property int pageSize: 6
|
||||
property int noOfPages: Math.ceil(RootStore.derivedAddressesList.count/pageSize)
|
||||
property int lastPageSize: RootStore.derivedAddressesList.count - ((noOfPages -1) * pageSize)
|
||||
|
@ -62,6 +75,13 @@ Item {
|
|||
// dimensions
|
||||
property int popupWidth: 359
|
||||
property int maxAddressWidth: 102
|
||||
|
||||
function runAction() {
|
||||
if (derivedAddresses.selectedKeyUidMigratedToKeycard)
|
||||
RootStore.authenticateUserAndDeriveAddressOnKeycardForPath(derivedAddresses.selectedKeyUid, derivedAddresses.selectedPath)
|
||||
else
|
||||
RootStore.authenticateUser()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -90,6 +110,7 @@ Item {
|
|||
property int pathSubFix: 0
|
||||
property bool hasActivity: false
|
||||
implicitWidth: parent.width
|
||||
visible: !_internal.showEnterPinPassButton
|
||||
color: "transparent"
|
||||
border.width: 1
|
||||
border.color: Theme.palette.baseColor2
|
||||
|
@ -117,6 +138,17 @@ Item {
|
|||
enabled: RootStore.derivedAddressesList.count > 0
|
||||
Component.onCompleted: derivedAddresses.pathSubFix = Qt.binding(function() { return pathSubFix})
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
visible: _internal.showEnterPinPassButton
|
||||
text: derivedAddresses.selectedKeyUidMigratedToKeycard || userProfile.isKeycardUser?
|
||||
qsTr("Enter PIN") :
|
||||
qsTr("Enter password")
|
||||
icon.name: derivedAddresses.enterPasswordIcon
|
||||
highlighted: focus
|
||||
|
||||
onClicked: _internal.runAction()
|
||||
}
|
||||
}
|
||||
|
||||
StatusPopupMenu {
|
||||
|
|
|
@ -14,6 +14,9 @@ StatusSelect {
|
|||
|
||||
property int addAccountType
|
||||
property string derivedFromAddress: ""
|
||||
property string selectedKeyUid: userProfile.keyUid
|
||||
property bool selectedKeyUidMigratedToKeycard: userProfile.isKeycardUser
|
||||
|
||||
|
||||
enum AddAccountType {
|
||||
GenerateNew,
|
||||
|
@ -24,7 +27,9 @@ StatusSelect {
|
|||
|
||||
function resetMe() {
|
||||
_internal.getGeneratedAccountsModel()
|
||||
addAccountType = SelectGeneratedAccount.AddAccountType.GenerateNew
|
||||
selectAccountType.addAccountType = SelectGeneratedAccount.AddAccountType.GenerateNew
|
||||
selectAccountType.selectedKeyUid = userProfile.keyUid
|
||||
selectAccountType.selectedKeyUidMigratedToKeycard = userProfile.isKeycardUser
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -55,16 +60,17 @@ StatusSelect {
|
|||
for (var row = 0; row < _internal.delegateModel.model.count; row++) {
|
||||
if (_internal.delegateModel.items.count > 0) {
|
||||
var item = _internal.delegateModel.items.get(row).model;
|
||||
generatedAccountsModel.append({"name": item.name, "iconName": item.iconName, "generatedModel": item.generatedModel, "derivedfrom": item.derivedfrom, "isHeader": false})
|
||||
generatedAccountsModel.append({"name": item.name, "iconName": item.iconName, "generatedModel": item.generatedModel, "derivedfrom": item.derivedfrom, "isHeader": false,
|
||||
"keyUid": item.keyUid, "migratedToKeycard": item.migratedToKeycard})
|
||||
if (row === 0 && _internal.delegateModel.model.count > 1) {
|
||||
generatedAccountsModel.append({"name": qsTr("Imported"), "iconName": "", "derivedfrom": "", "isHeader": true})
|
||||
generatedAccountsModel.append({"name": qsTr("Imported"), "iconName": "", "derivedfrom": "", "isHeader": true, "keyUid": "", "migratedToKeycard": false})
|
||||
}
|
||||
}
|
||||
}
|
||||
generatedAccountsModel.append({"name": qsTr("Add new"), "iconName": "", "derivedfrom": "", "isHeader": true})
|
||||
generatedAccountsModel.append({"name": _internal.importSeedPhraseString, "iconName": "seed-phrase", "derivedfrom": "", "isHeader": false})
|
||||
generatedAccountsModel.append({"name": _internal.importPrivateKeyString, "iconName": "password", "derivedfrom": "", "isHeader": false})
|
||||
generatedAccountsModel.append({"name": _internal.addWatchOnlyAccountString, "iconName": "show", "derivedfrom": "", "isHeader": false})
|
||||
generatedAccountsModel.append({"name": qsTr("Add new"), "iconName": "", "derivedfrom": "", "isHeader": true, "keyUid": "", "migratedToKeycard": false})
|
||||
generatedAccountsModel.append({"name": _internal.importSeedPhraseString, "iconName": "seed-phrase", "derivedfrom": "", "isHeader": false, "keyUid": "", "migratedToKeycard": false})
|
||||
generatedAccountsModel.append({"name": _internal.importPrivateKeyString, "iconName": "password", "derivedfrom": "", "isHeader": false, "keyUid": "", "migratedToKeycard": false})
|
||||
generatedAccountsModel.append({"name": _internal.addWatchOnlyAccountString, "iconName": "show", "derivedfrom": "", "isHeader": false, "keyUid": "", "migratedToKeycard": false})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +126,9 @@ StatusSelect {
|
|||
selectedItem.enabled = !model.isHeader
|
||||
|
||||
selectAccountType.derivedFromAddress = model.derivedfrom
|
||||
selectAccountType.selectedKeyUid = model.keyUid
|
||||
selectAccountType.selectedKeyUidMigratedToKeycard = model.migratedToKeycard
|
||||
|
||||
selectMenu.close()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
|
|
|
@ -27,7 +27,8 @@ StatusModal {
|
|||
property var emojiPopup: null
|
||||
|
||||
header.title: qsTr("Generate an account")
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
closePolicy: nextButton.loading? Popup.NoAutoClose : Popup.CloseOnEscape
|
||||
hasCloseButton: !nextButton.loading
|
||||
|
||||
signal afterAddAccount()
|
||||
|
||||
|
@ -45,11 +46,21 @@ StatusModal {
|
|||
onUserAuthenticationSuccess: {
|
||||
validationError.text = ""
|
||||
d.password = password
|
||||
d.getDerivedAddressList()
|
||||
RootStore.loggedInUserAuthenticated = true
|
||||
if (d.selectedAccountType === SelectGeneratedAccount.AddAccountType.ImportPrivateKey) {
|
||||
d.generateNewAccount()
|
||||
}
|
||||
else {
|
||||
if (!d.selectedKeyUidMigratedToKeycard) {
|
||||
d.getDerivedAddressList()
|
||||
}
|
||||
}
|
||||
}
|
||||
onUserAuthentiactionFail: {
|
||||
d.password = ""
|
||||
RootStore.loggedInUserAuthenticated = false
|
||||
validationError.text = qsTr("An authentication failed")
|
||||
nextButton.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,13 +71,24 @@ StatusModal {
|
|||
readonly property int pageNumber: 1
|
||||
|
||||
property string password: ""
|
||||
property int selectedAccountType: SelectGeneratedAccount.AddAccountType.GenerateNew
|
||||
property int selectedAccountType: RootStore.defaultSelectedType
|
||||
property string selectedAccountDerivedFromAddress: ""
|
||||
property string selectedKeyUid: RootStore.defaultSelectedKeyUid
|
||||
property bool selectedKeyUidMigratedToKeycard: RootStore.defaultSelectedKeyUidMigratedToKeycard
|
||||
property string selectedPath: ""
|
||||
property string selectedAddress: ""
|
||||
property bool selectedAddressAvailable: true
|
||||
|
||||
readonly property bool authenticationNeeded: d.selectedAccountType !== SelectGeneratedAccount.AddAccountType.WatchOnly &&
|
||||
d.password === ""
|
||||
property string addAccountIcon: ""
|
||||
|
||||
|
||||
|
||||
property bool isLoading: RootStore.derivedAddressesLoading
|
||||
onIsLoadingChanged: {
|
||||
if(!isLoading && nextButton.loading) {
|
||||
d.generateNewAccount()
|
||||
}
|
||||
}
|
||||
|
||||
function getDerivedAddressList() {
|
||||
if(d.selectedAccountType === SelectGeneratedAccount.AddAccountType.ImportSeedPhrase
|
||||
|
@ -83,7 +105,6 @@ StatusModal {
|
|||
|
||||
function generateNewAccount() {
|
||||
// TODO the loading doesn't work because the function freezes the view. Might need to use threads
|
||||
nextButton.loading = true
|
||||
if (!advancedSelection.validate()) {
|
||||
Global.playErrorSound()
|
||||
return nextButton.loading = false
|
||||
|
@ -93,9 +114,17 @@ StatusModal {
|
|||
|
||||
switch(d.selectedAccountType) {
|
||||
case SelectGeneratedAccount.AddAccountType.GenerateNew:
|
||||
if (d.selectedKeyUidMigratedToKeycard) {
|
||||
errMessage = RootStore.addNewWalletAccountGeneratedFromKeycard(Constants.generatedWalletType,
|
||||
accountNameInput.text,
|
||||
colorSelectionGrid.selectedColor,
|
||||
accountNameInput.input.asset.emoji)
|
||||
}
|
||||
else {
|
||||
errMessage = RootStore.generateNewAccount(d.password, accountNameInput.text, colorSelectionGrid.selectedColor,
|
||||
accountNameInput.input.asset.emoji, advancedSelection.expandableItem.completePath,
|
||||
advancedSelection.expandableItem.derivedFromAddress)
|
||||
}
|
||||
break
|
||||
case SelectGeneratedAccount.AddAccountType.ImportSeedPhrase:
|
||||
errMessage = RootStore.addAccountsFromSeed(advancedSelection.expandableItem.mnemonicText, d.password,
|
||||
|
@ -123,9 +152,16 @@ StatusModal {
|
|||
}
|
||||
|
||||
function nextButtonClicked() {
|
||||
nextButton.loading = true
|
||||
if (d.authenticationNeeded) {
|
||||
d.password = ""
|
||||
RootStore.authenticateUser()
|
||||
if (d.selectedKeyUidMigratedToKeycard &&
|
||||
d.selectedAccountType === SelectGeneratedAccount.AddAccountType.GenerateNew) {
|
||||
RootStore.authenticateUserAndDeriveAddressOnKeycardForPath(d.selectedKeyUid, d.selectedPath)
|
||||
}
|
||||
else {
|
||||
RootStore.authenticateUser()
|
||||
}
|
||||
}
|
||||
else {
|
||||
d.generateNewAccount()
|
||||
|
@ -134,6 +170,7 @@ StatusModal {
|
|||
}
|
||||
|
||||
onOpened: {
|
||||
RootStore.loggedInUserAuthenticated = false
|
||||
d.addAccountIcon = "password"
|
||||
if (RootStore.loggedInUserUsesBiometricLogin()) {
|
||||
d.addAccountIcon = "touch-id"
|
||||
|
@ -148,6 +185,7 @@ StatusModal {
|
|||
}
|
||||
|
||||
onClosed: {
|
||||
RootStore.loggedInUserAuthenticated = false
|
||||
d.password = ""
|
||||
validationError.text = ""
|
||||
accountNameInput.reset()
|
||||
|
@ -157,7 +195,7 @@ StatusModal {
|
|||
|
||||
contentItem: StatusScrollView {
|
||||
id: scroll
|
||||
width: popup.width
|
||||
width: root.width
|
||||
topPadding: Style.current.halfPadding
|
||||
bottomPadding: Style.current.halfPadding
|
||||
leftPadding: Style.current.padding
|
||||
|
@ -214,6 +252,7 @@ StatusModal {
|
|||
StatusColorSelectorGrid {
|
||||
id: colorSelectionGrid
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
enabled: accountNameInput.valid
|
||||
titleText: qsTr("color").toUpperCase()
|
||||
}
|
||||
|
||||
|
@ -232,13 +271,24 @@ StatusModal {
|
|||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
enabled: accountNameInput.valid
|
||||
|
||||
primaryText: qsTr("Advanced")
|
||||
type: StatusExpandableItem.Type.Tertiary
|
||||
expandable: true
|
||||
expandableComponent: AdvancedAddAccountView {
|
||||
width: parent.width
|
||||
onCalculateDerivedPath: d.getDerivedAddressList()
|
||||
enterPasswordIcon: d.addAccountIcon
|
||||
onCalculateDerivedPath: {
|
||||
if (d.selectedKeyUidMigratedToKeycard) {
|
||||
d.password = ""
|
||||
validationError.text = ""
|
||||
RootStore.loggedInUserAuthenticated = false
|
||||
}
|
||||
else{
|
||||
d.getDerivedAddressList()
|
||||
}
|
||||
}
|
||||
onEnterPressed: {
|
||||
if (nextButton.enabled) {
|
||||
nextButton.clicked(null)
|
||||
|
@ -246,12 +296,14 @@ StatusModal {
|
|||
}
|
||||
}
|
||||
|
||||
onAddAccountTypeChanged: {
|
||||
d.selectedAccountType = addAccountType
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
d.selectedAccountType = addAccountType
|
||||
d.selectedAccountType = Qt.binding(() => addAccountType)
|
||||
d.selectedAccountDerivedFromAddress = Qt.binding(() => derivedFromAddress)
|
||||
d.selectedKeyUid = Qt.binding(() => selectedKeyUid)
|
||||
d.selectedKeyUidMigratedToKeycard = Qt.binding(() => selectedKeyUidMigratedToKeycard)
|
||||
d.selectedPath = Qt.binding(() => path)
|
||||
d.selectedAddress = Qt.binding(() => selectedAddress)
|
||||
d.selectedAddressAvailable = Qt.binding(() => selectedAddressAvailable)
|
||||
advancedSelection.isValid = Qt.binding(() => isValid)
|
||||
}
|
||||
}
|
||||
|
@ -264,9 +316,6 @@ StatusModal {
|
|||
id: nextButton
|
||||
|
||||
text: {
|
||||
if (d.authenticationNeeded) {
|
||||
return qsTr("Authenticate")
|
||||
}
|
||||
if (loading) {
|
||||
return qsTr("Loading...")
|
||||
}
|
||||
|
@ -275,19 +324,18 @@ StatusModal {
|
|||
|
||||
|
||||
enabled: {
|
||||
if (d.authenticationNeeded) {
|
||||
return true
|
||||
if (!accountNameInput.valid) {
|
||||
return false
|
||||
}
|
||||
if (loading) {
|
||||
return false
|
||||
}
|
||||
return accountNameInput.text !== "" && advancedSelection.isValid
|
||||
return advancedSelection.isValid
|
||||
}
|
||||
|
||||
icon.name: d.authenticationNeeded? d.addAccountIcon : ""
|
||||
highlighted: focus
|
||||
|
||||
Keys.onReturnPressed: d.nextButtonClicked()
|
||||
onClicked : d.nextButtonClicked()
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,9 +5,17 @@ import QtQuick 2.13
|
|||
import utils 1.0
|
||||
import shared.stores 1.0 as SharedStore
|
||||
|
||||
import "../panels"
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
readonly property int defaultSelectedType: SelectGeneratedAccount.AddAccountType.GenerateNew
|
||||
readonly property string defaultSelectedKeyUid: userProfile.keyUid
|
||||
readonly property bool defaultSelectedKeyUidMigratedToKeycard: userProfile.isKeycardUser
|
||||
|
||||
property bool loggedInUserAuthenticated: false
|
||||
|
||||
property string backButtonName: ""
|
||||
property var currentAccount: Constants.isCppApp ? walletSectionAccounts.currentAccount: walletSectionCurrent
|
||||
property var accounts: walletSectionAccounts.model
|
||||
|
@ -114,6 +122,10 @@ QtObject {
|
|||
return walletSectionAccounts.generateNewAccount(password, accountName, color, emoji, path, derivedFrom)
|
||||
}
|
||||
|
||||
function addNewWalletAccountGeneratedFromKeycard(accountType, accountName, color, emoji) {
|
||||
return walletSectionAccounts.addNewWalletAccountGeneratedFromKeycard(accountType, accountName, color, emoji)
|
||||
}
|
||||
|
||||
function addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji) {
|
||||
return walletSectionAccounts.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji)
|
||||
}
|
||||
|
@ -234,4 +246,16 @@ QtObject {
|
|||
function loggedInUserIsKeycardUser() {
|
||||
return userProfile.isKeycardUser
|
||||
}
|
||||
|
||||
function createSharedKeycardModule() {
|
||||
walletSectionAccounts.createSharedKeycardModule()
|
||||
}
|
||||
|
||||
function destroySharedKeycarModule() {
|
||||
walletSectionAccounts.destroySharedKeycarModule()
|
||||
}
|
||||
|
||||
function authenticateUserAndDeriveAddressOnKeycardForPath(keyUid, derivationPath) {
|
||||
walletSectionAccounts.authenticateUserAndDeriveAddressOnKeycardForPath(keyUid, derivationPath)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,11 @@ ColumnLayout {
|
|||
id: advancedSection
|
||||
|
||||
property int addAccountType: SelectGeneratedAccount.AddAccountType.GenerateNew
|
||||
property string selectedKeyUid: RootStore.defaultSelectedKeyUid
|
||||
property bool selectedKeyUidMigratedToKeycard: RootStore.defaultSelectedKeyUidMigratedToKeycard
|
||||
property string selectedAddress: ""
|
||||
property bool selectedAddressAvailable: true
|
||||
property string enterPasswordIcon: ""
|
||||
property string derivedFromAddress: ""
|
||||
property string mnemonicText: ""
|
||||
property alias privateKey: importPrivateKeyPanel.text
|
||||
|
@ -100,6 +105,8 @@ ColumnLayout {
|
|||
Component.onCompleted: {
|
||||
advancedSection.addAccountType = Qt.binding(function() {return addAccountType})
|
||||
advancedSection.derivedFromAddress = Qt.binding(function() {return derivedFromAddress})
|
||||
advancedSection.selectedKeyUid = Qt.binding(function() {return selectedKeyUid})
|
||||
advancedSection.selectedKeyUidMigratedToKeycard = Qt.binding(function() {return selectedKeyUidMigratedToKeycard})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,7 +162,18 @@ ColumnLayout {
|
|||
id: derivedAddressesPanel
|
||||
Layout.preferredWidth: ((parent.width - (Style.current.bigPadding/2))/2)
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Component.onCompleted: advancedSection.pathSubFix = Qt.binding(function() { return derivedAddressesPanel.pathSubFix})
|
||||
|
||||
selectedAccountType: advancedSection.addAccountType
|
||||
selectedKeyUid: advancedSection.selectedKeyUid
|
||||
selectedKeyUidMigratedToKeycard: advancedSection.selectedKeyUidMigratedToKeycard
|
||||
selectedPath: advancedSection.path
|
||||
enterPasswordIcon: advancedSection.enterPasswordIcon
|
||||
|
||||
Component.onCompleted: {
|
||||
advancedSection.selectedAddress = Qt.binding(function() { return derivedAddressesPanel.selectedAddress})
|
||||
advancedSection.selectedAddressAvailable = Qt.binding(function() { return derivedAddressesPanel.selectedAddressAvailable})
|
||||
advancedSection.pathSubFix = Qt.binding(function() { return derivedAddressesPanel.pathSubFix})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import utils 1.0
|
|||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.controls 1.0
|
||||
import shared.popups.keycard 1.0
|
||||
|
||||
import "../controls"
|
||||
import "../popups"
|
||||
|
@ -30,11 +31,33 @@ Rectangle {
|
|||
|
||||
color: Style.current.secondaryMenuBackground
|
||||
|
||||
AddAccountModal {
|
||||
Loader {
|
||||
id: addAccountModal
|
||||
anchors.centerIn: parent
|
||||
onAfterAddAccount: root.onAfterAddAccount()
|
||||
emojiPopup: root.emojiPopup
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
||||
function open() {
|
||||
if (!active) {
|
||||
RootStore.createSharedKeycardModule()
|
||||
active = true
|
||||
}
|
||||
item.open()
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (item) {
|
||||
RootStore.destroySharedKeycarModule()
|
||||
item.close()
|
||||
}
|
||||
active = false
|
||||
}
|
||||
|
||||
sourceComponent: AddAccountModal {
|
||||
anchors.centerIn: parent
|
||||
onAfterAddAccount: root.onAfterAddAccount()
|
||||
emojiPopup: root.emojiPopup
|
||||
onClosed: addAccountModal.close()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
|
@ -664,6 +664,7 @@ QtObject {
|
|||
readonly property bool isCppApp: typeof cppApp !== "undefined" ? cppApp : false
|
||||
|
||||
readonly property string existingAccountError: "account already exists"
|
||||
readonly property string wrongDerivationPathError: "error parsing derivation path"
|
||||
|
||||
enum TransactionStatus {
|
||||
Failure = 0,
|
||||
|
|
|
@ -735,11 +735,11 @@ QtObject {
|
|||
}
|
||||
|
||||
function isInvalidPath(msg) {
|
||||
return msg.includes("error parsing derivation path")
|
||||
return msg.includes(Constants.wrongDerivationPathError)
|
||||
}
|
||||
|
||||
function accountAlreadyExistsError(msg) {
|
||||
return msg.includes("account already exists")
|
||||
return msg.includes(Constants.existingAccountError)
|
||||
}
|
||||
|
||||
// See also: backend/interpret/cropped_image.nim
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit bb4237f616ea17fb7083b91bf20ceed11ca253aa
|
||||
Subproject commit c2b17acc074fa2630fd5a5c69aaa8ac8099d5c65
|
Loading…
Reference in New Issue