feat: some limits for wallet section
Added limitations: - allowed adding of max 20 accounts - allowed adding of max 3 watch only accounts - allowed adding of max 5 key pairs (including the profile key pair) - allowed adding of max 20 saved addresses per mode Closes #15934
This commit is contained in:
parent
effd676a16
commit
77ca8761a6
|
@ -52,3 +52,6 @@ proc createOrUpdateSavedAddress*(self: Controller, name: string, address: string
|
|||
|
||||
proc deleteSavedAddress*(self: Controller, address: string) =
|
||||
self.savedAddressService.deleteSavedAddress(address)
|
||||
|
||||
proc remainingCapacityForSavedAddresses*(self: Controller): int =
|
||||
return self.savedAddressService.remainingCapacityForSavedAddresses()
|
||||
|
|
|
@ -39,6 +39,9 @@ method savedAddressNameExists*(self: AccessInterface, name: string): bool {.base
|
|||
method getSavedAddressAsJson*(self: AccessInterface, address: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method remainingCapacityForSavedAddresses*(self: AccessInterface): int {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
type
|
||||
## Abstract class (concept) which must be implemented by object/s used in this
|
||||
## module.
|
||||
|
|
|
@ -103,4 +103,7 @@ method getSavedAddressAsJson*(self: Module, address: string): string =
|
|||
"chainShortNames": saDto.chainShortNames,
|
||||
"isTest": saDto.isTest,
|
||||
}
|
||||
return $jsonObj
|
||||
return $jsonObj
|
||||
|
||||
method remainingCapacityForSavedAddresses*(self: Module): int =
|
||||
return self.controller.remainingCapacityForSavedAddresses()
|
|
@ -58,4 +58,7 @@ QtObject:
|
|||
return self.delegate.savedAddressNameExists(name)
|
||||
|
||||
proc getSavedAddressAsJson*(self: View, address: string): string {.slot.} =
|
||||
return self.delegate.getSavedAddressAsJson(address)
|
||||
return self.delegate.getSavedAddressAsJson(address)
|
||||
|
||||
proc remainingCapacityForSavedAddresses*(self: View): int {.slot.} =
|
||||
return self.delegate.remainingCapacityForSavedAddresses()
|
|
@ -253,4 +253,13 @@ proc resolveSuggestedPathForKeypair*(self: Controller, keyUid: string): string =
|
|||
return self.walletAccountService.resolveSuggestedPathForKeypair(keyUid)
|
||||
|
||||
proc isChecksumValidForAddress*(self: Controller, address: string): bool =
|
||||
return self.walletAccountService.isChecksumValidForAddress(address)
|
||||
return self.walletAccountService.isChecksumValidForAddress(address)
|
||||
|
||||
proc remainingAccountCapacity*(self: Controller): int =
|
||||
return self.walletAccountService.remainingAccountCapacity()
|
||||
|
||||
proc remainingKeypairCapacity*(self: Controller): int =
|
||||
return self.walletAccountService.remainingKeypairCapacity()
|
||||
|
||||
proc remainingWatchOnlyAccountCapacity*(self: Controller): int =
|
||||
return self.walletAccountService.remainingWatchOnlyAccountCapacity()
|
|
@ -110,6 +110,15 @@ method savedAddressDeleted*(self: AccessInterface, address: string, errorMsg: st
|
|||
method isChecksumValidForAddress*(self: AccessInterface, address: string): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method remainingAccountCapacity*(self: AccessInterface): int {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method remainingKeypairCapacity*(self: AccessInterface): int {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method remainingWatchOnlyAccountCapacity*(self: AccessInterface): int {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
type
|
||||
DelegateInterface* = concept c
|
||||
c.onAddAccountModuleLoaded()
|
||||
|
|
|
@ -739,4 +739,13 @@ method buildNewSeedPhraseKeypairAndAddItToOrigin*[T](self: Module[T]) =
|
|||
method isChecksumValidForAddress*[T](self: Module[T], address: string): bool =
|
||||
return self.controller.isChecksumValidForAddress(address)
|
||||
|
||||
method remainingAccountCapacity*[T](self: Module[T]): int =
|
||||
return self.controller.remainingAccountCapacity()
|
||||
|
||||
method remainingKeypairCapacity*[T](self: Module[T]): int =
|
||||
return self.controller.remainingKeypairCapacity()
|
||||
|
||||
method remainingWatchOnlyAccountCapacity*[T](self: Module[T]): int =
|
||||
return self.controller.remainingWatchOnlyAccountCapacity()
|
||||
|
||||
{.pop.}
|
||||
|
|
|
@ -365,4 +365,13 @@ QtObject:
|
|||
self.setDisablePopup(false)
|
||||
|
||||
proc isChecksumValidForAddress*(self: View, address: string): bool {.slot.} =
|
||||
return self.delegate.isChecksumValidForAddress(address)
|
||||
return self.delegate.isChecksumValidForAddress(address)
|
||||
|
||||
proc remainingAccountCapacity*(self: View): int {.slot.} =
|
||||
return self.delegate.remainingAccountCapacity()
|
||||
|
||||
proc remainingKeypairCapacity*(self: View): int {.slot.} =
|
||||
return self.delegate.remainingKeypairCapacity()
|
||||
|
||||
proc remainingWatchOnlyAccountCapacity*(self: View): int {.slot.} =
|
||||
return self.delegate.remainingWatchOnlyAccountCapacity()
|
|
@ -171,4 +171,13 @@ QtObject:
|
|||
except Exception as e:
|
||||
error "onDeleteSavedAddress", msg = e.msg
|
||||
arg.errorMsg = e.msg
|
||||
self.updateAddresses(SIGNAL_SAVED_ADDRESS_DELETED, arg)
|
||||
self.updateAddresses(SIGNAL_SAVED_ADDRESS_DELETED, arg)
|
||||
|
||||
proc remainingCapacityForSavedAddresses*(self: Service): int =
|
||||
try:
|
||||
let response = backend.remainingCapacityForSavedAddresses(self.areTestNetworksEnabled())
|
||||
if not response.error.isNil:
|
||||
raise newException(CatchableError, response.error.message)
|
||||
return response.result.getInt
|
||||
except Exception as e:
|
||||
error "error: ", procName="remainingCapacityForSavedAddresses", errName=e.name, errDesription=e.msg
|
|
@ -811,3 +811,30 @@ proc resolveSuggestedPathForKeypair*(self: Service, keyUid: string): string =
|
|||
return response.result.getStr
|
||||
except Exception as e:
|
||||
error "error: ", procName="resolveSuggestedPathForKeypair", errName=e.name, errDesription=e.msg
|
||||
|
||||
proc remainingAccountCapacity*(self: Service): int =
|
||||
try:
|
||||
let response = status_go_accounts.remainingAccountCapacity()
|
||||
if not response.error.isNil:
|
||||
raise newException(CatchableError, response.error.message)
|
||||
return response.result.getInt
|
||||
except Exception as e:
|
||||
error "error: ", procName="remainingAccountCapacity", errName=e.name, errDesription=e.msg
|
||||
|
||||
proc remainingKeypairCapacity*(self: Service): int =
|
||||
try:
|
||||
let response = status_go_accounts.remainingKeypairCapacity()
|
||||
if not response.error.isNil:
|
||||
raise newException(CatchableError, response.error.message)
|
||||
return response.result.getInt
|
||||
except Exception as e:
|
||||
error "error: ", procName="remainingKeypairCapacity", errName=e.name, errDesription=e.msg
|
||||
|
||||
proc remainingWatchOnlyAccountCapacity*(self: Service): int =
|
||||
try:
|
||||
let response = status_go_accounts.remainingWatchOnlyAccountCapacity()
|
||||
if not response.error.isNil:
|
||||
raise newException(CatchableError, response.error.message)
|
||||
return response.result.getInt
|
||||
except Exception as e:
|
||||
error "error: ", procName="remainingWatchOnlyAccountCapacity", errName=e.name, errDesription=e.msg
|
|
@ -420,3 +420,15 @@ proc getNumOfAddressesToGenerateForKeypair*(keyUID: string): RpcResponse[JsonNod
|
|||
proc resolveSuggestedPathForKeypair*(keyUID: string): RpcResponse[JsonNode] =
|
||||
let payload = %* [keyUID]
|
||||
result = core.callPrivateRPC("accounts_resolveSuggestedPathForKeypair", payload)
|
||||
|
||||
proc remainingAccountCapacity*(): RpcResponse[JsonNode] =
|
||||
let payload = %* []
|
||||
return core.callPrivateRPC("accounts_remainingAccountCapacity", payload)
|
||||
|
||||
proc remainingKeypairCapacity*(): RpcResponse[JsonNode] =
|
||||
let payload = %* []
|
||||
return core.callPrivateRPC("accounts_remainingKeypairCapacity", payload)
|
||||
|
||||
proc remainingWatchOnlyAccountCapacity*(): RpcResponse[JsonNode] =
|
||||
let payload = %* []
|
||||
return core.callPrivateRPC("accounts_remainingWatchOnlyAccountCapacity", payload)
|
|
@ -76,6 +76,12 @@ rpc(deleteSavedAddress, "wakuext"):
|
|||
rpc(getSavedAddresses, "wakuext"):
|
||||
discard
|
||||
|
||||
rpc(getSavedAddressesPerMode, "wakuext"):
|
||||
isTest: bool
|
||||
|
||||
rpc(remainingCapacityForSavedAddresses, "wakuext"):
|
||||
isTest: bool
|
||||
|
||||
rpc(checkConnected, "wallet"):
|
||||
discard
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import StatusQ.Core.Backpressure 0.1
|
|||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Popups.Dialog 0.1
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
|
@ -283,6 +284,11 @@ StatusModal {
|
|||
|| event !== undefined && event.key !== Qt.Key_Return && event.key !== Qt.Key_Enter)
|
||||
return
|
||||
|
||||
if (!d.editMode && root.store.remainingCapacityForSavedAddresses() === 0) {
|
||||
limitPopup.active = true
|
||||
return
|
||||
}
|
||||
|
||||
root.store.createOrUpdateSavedAddress(d.name, d.address, d.ens, d.colorId, d.chainShortNames)
|
||||
root.close()
|
||||
}
|
||||
|
@ -365,6 +371,34 @@ StatusModal {
|
|||
|
||||
spacing: Style.current.xlPadding
|
||||
|
||||
Loader {
|
||||
id: limitPopup
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: StatusDialog {
|
||||
width: root.width - 2*Style.current.padding
|
||||
|
||||
title: Constants.walletConstants.maxNumberOfSavedAddressesTitle
|
||||
|
||||
StatusBaseText {
|
||||
anchors.fill: parent
|
||||
text: Constants.walletConstants.maxNumberOfSavedAddressesContent
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
standardButtons: Dialog.Ok
|
||||
|
||||
onClosed: {
|
||||
limitPopup.active = false
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
limitPopup.item.open()
|
||||
}
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: nameInput
|
||||
implicitWidth: d.componentWidth
|
||||
|
|
|
@ -369,6 +369,10 @@ QtObject {
|
|||
return walletSectionSavedAddresses.savedAddressNameExists(name)
|
||||
}
|
||||
|
||||
function remainingCapacityForSavedAddresses() {
|
||||
return walletSectionSavedAddresses.remainingCapacityForSavedAddresses()
|
||||
}
|
||||
|
||||
function toggleNetwork(chainId) {
|
||||
networksModule.toggleNetwork(chainId)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import QtQuick.Layouts 1.15
|
|||
import StatusQ.Core 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups.Dialog 0.1
|
||||
|
||||
import utils 1.0
|
||||
import shared.popups 1.0
|
||||
|
@ -18,6 +19,12 @@ StatusModal {
|
|||
|
||||
property AddAccountStore store: AddAccountStore { }
|
||||
|
||||
enum LimitWarning {
|
||||
Accounts,
|
||||
Keypairs,
|
||||
WatchOnlyAccounts
|
||||
}
|
||||
|
||||
width: Constants.addAccountPopup.popupWidth
|
||||
|
||||
closePolicy: root.store.disablePopup? Popup.NoAutoClose : Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
@ -27,6 +34,8 @@ StatusModal {
|
|||
|
||||
onOpened: {
|
||||
root.store.resetStoreValues()
|
||||
|
||||
root.store.showLimitPopup.connect(limitPopup.showPopup)
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
|
@ -55,6 +64,59 @@ StatusModal {
|
|||
implicitHeight: loader.implicitHeight
|
||||
width: scrollView.availableWidth
|
||||
|
||||
Loader {
|
||||
id: limitPopup
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
||||
property string title
|
||||
property string content
|
||||
|
||||
function showPopup(warningType) {
|
||||
if (warningType === AddAccountPopup.LimitWarning.Accounts) {
|
||||
limitPopup.title = Constants.walletConstants.maxNumberOfAccountsTitle
|
||||
limitPopup.content = Constants.walletConstants.maxNumberOfAccountsContent
|
||||
} else if (warningType === AddAccountPopup.LimitWarning.Keypairs) {
|
||||
limitPopup.title = Constants.walletConstants.maxNumberOfKeypairsTitle
|
||||
limitPopup.content = Constants.walletConstants.maxNumberOfKeypairsContent
|
||||
} else if (warningType === AddAccountPopup.LimitWarning.WatchOnlyAccounts) {
|
||||
limitPopup.title = Constants.walletConstants.maxNumberOfWatchOnlyAccountsTitle
|
||||
limitPopup.content = Constants.walletConstants.maxNumberOfSavedAddressesContent
|
||||
} else {
|
||||
console.error("unsupported warning type")
|
||||
return
|
||||
}
|
||||
|
||||
limitPopup.active = true
|
||||
}
|
||||
|
||||
sourceComponent: StatusDialog {
|
||||
width: root.width - 2*Style.current.padding
|
||||
|
||||
property string contentText
|
||||
|
||||
title: Constants.walletConstants.maxNumberOfAccountsTitle
|
||||
|
||||
StatusBaseText {
|
||||
anchors.fill: parent
|
||||
text: contentText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
standardButtons: Dialog.Ok
|
||||
|
||||
onClosed: {
|
||||
limitPopup.active = false
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
limitPopup.item.title = limitPopup.title
|
||||
limitPopup.item.contentText = limitPopup.content
|
||||
limitPopup.item.open()
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
width: parent.width
|
||||
|
@ -93,6 +155,13 @@ StatusModal {
|
|||
id: mainComponent
|
||||
Main {
|
||||
store: root.store
|
||||
|
||||
onWatchOnlyAccountsLimitReached: {
|
||||
limitPopup.showPopup(AddAccountPopup.LimitWarning.WatchOnlyAccounts)
|
||||
}
|
||||
onKeypairLimitReached: {
|
||||
limitPopup.showPopup(AddAccountPopup.LimitWarning.Keypairs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ Item {
|
|||
|
||||
property AddAccountStore store
|
||||
|
||||
signal watchOnlyAccountsLimitReached()
|
||||
signal keypairLimitReached()
|
||||
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
Component.onCompleted: {
|
||||
|
@ -194,7 +197,17 @@ Item {
|
|||
enabled: !root.store.editMode
|
||||
|
||||
onOriginSelected: {
|
||||
if (keyUid === Constants.appTranslatableConstants.addAccountLabelOptionAddWatchOnlyAcc) {
|
||||
if (root.store.remainingWatchOnlyAccountCapacity() === 0) {
|
||||
root.watchOnlyAccountsLimitReached()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (keyUid === Constants.appTranslatableConstants.addAccountLabelOptionAddNewMasterKey) {
|
||||
if (root.store.remainingKeypairCapacity() === 0) {
|
||||
root.keypairLimitReached()
|
||||
return
|
||||
}
|
||||
root.store.currentState.doSecondaryAction()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -54,6 +54,8 @@ BasePopupStore {
|
|||
Constants.addAccountPopup.predefinedPaths.ethereumLedgerLive
|
||||
]
|
||||
|
||||
signal showLimitPopup(int warningType)
|
||||
|
||||
function resetStoreValues() {
|
||||
root.enteredSeedPhraseIsValid = false
|
||||
root.enteredPrivateKeyIsValid = false
|
||||
|
@ -91,10 +93,20 @@ BasePopupStore {
|
|||
}
|
||||
|
||||
if(!event) {
|
||||
if (!root.editMode && root.remainingAccountCapacity() === 0) {
|
||||
root.showLimitPopup(0)
|
||||
return
|
||||
}
|
||||
|
||||
root.currentState.doPrimaryAction()
|
||||
}
|
||||
else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
event.accepted = true
|
||||
if (!root.editMode && root.remainingAccountCapacity() === 0) {
|
||||
root.showLimitPopup(0)
|
||||
return
|
||||
}
|
||||
|
||||
root.currentState.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +171,18 @@ BasePopupStore {
|
|||
return root.addAccountModule.isChecksumValidForAddress(address)
|
||||
}
|
||||
|
||||
function remainingAccountCapacity() {
|
||||
return root.addAccountModule.remainingAccountCapacity()
|
||||
}
|
||||
|
||||
function remainingKeypairCapacity() {
|
||||
return root.addAccountModule.remainingKeypairCapacity()
|
||||
}
|
||||
|
||||
function remainingWatchOnlyAccountCapacity() {
|
||||
return root.addAccountModule.remainingWatchOnlyAccountCapacity()
|
||||
}
|
||||
|
||||
validSeedPhrase: function(seedPhrase) {
|
||||
return root.addAccountModule.validSeedPhrase(seedPhrase)
|
||||
}
|
||||
|
|
|
@ -873,6 +873,20 @@ QtObject {
|
|||
readonly property string market: "market"
|
||||
}
|
||||
|
||||
readonly property QtObject walletConstants: QtObject {
|
||||
readonly property string maxNumberOfAccountsTitle: qsTr("Limit of 20 accounts reached")
|
||||
readonly property string maxNumberOfAccountsContent: qsTr("Remove any account to add a new one.")
|
||||
|
||||
readonly property string maxNumberOfKeypairsTitle: qsTr("Limit of 5 key pairs reached")
|
||||
readonly property string maxNumberOfKeypairsContent: qsTr("Remove key pair to add a new one.")
|
||||
|
||||
readonly property string maxNumberOfWatchOnlyAccountsTitle: qsTr("Limit of 3 watched addresses reached")
|
||||
readonly property string maxNumberOfWatchOnlyAccountsContent: qsTr("Remove a watched address to add a new one.")
|
||||
|
||||
readonly property string maxNumberOfSavedAddressesTitle: qsTr("Limit of 20 saved addresses reached")
|
||||
readonly property string maxNumberOfSavedAddressesContent: qsTr("Remove a saved address to add a new one.")
|
||||
}
|
||||
|
||||
enum ConnectionStatus {
|
||||
Success = 0,
|
||||
Failure = 1,
|
||||
|
|
Loading…
Reference in New Issue