feat(wallet): in app tx related notifications improvements

Closes #16338
This commit is contained in:
Sale Djenic 2024-11-20 08:34:05 +01:00
parent 300bf4ac34
commit 9ca7167f71
11 changed files with 563 additions and 325 deletions

View File

@ -2,14 +2,14 @@ import NimQml, tables, json, sequtils, stew/shims/strformat, marshal, times, chr
import io_interface, view, controller, chat_search_item, chat_search_model
import ephemeral_notification_item, ephemeral_notification_model
import ../shared_models/[user_item, member_item, member_model, section_item, section_model, section_details]
import ../shared_models/[color_hash_item, color_hash_model]
import ../shared_modules/keycard_popup/module as keycard_shared_module
import ../../global/app_sections_config as conf
import ../../global/app_signals
import ../../global/global_singleton
import ../../global/utils as utils
import ../../../constants
import app/modules/shared_models/[user_item, member_item, member_model, section_item, section_model, section_details]
import app/modules/shared_models/[color_hash_item, color_hash_model]
import app/modules/shared_modules/keycard_popup/module as keycard_shared_module
import app/global/app_sections_config as conf
import app/global/app_signals
import app/global/global_singleton
import app/global/utils as utils
import constants
import chat_section/model as chat_model
import chat_section/item as chat_item
@ -27,51 +27,51 @@ import communities/tokens/models/token_model
import network_connection/module as network_connection_module
import shared_urls/module as shared_urls_module
import ../../../app_service/service/contacts/dto/contacts
import ../../../app_service/service/community_tokens/community_collectible_owner
import app_service/service/contacts/dto/contacts
import app_service/service/community_tokens/community_collectible_owner
import ../../../app_service/service/keychain/service as keychain_service
import ../../../app_service/service/chat/service as chat_service
import ../../../app_service/service/community/service as community_service
import ../../../app_service/service/message/service as message_service
import ../../../app_service/service/token/service as token_service
import ../../../app_service/service/collectible/service as collectible_service
import ../../../app_service/service/currency/service as currency_service
import ../../../app_service/service/ramp/service as ramp_service
import ../../../app_service/service/transaction/service as transaction_service
import ../../../app_service/service/wallet_account/service as wallet_account_service
import ../../../app_service/service/provider/service as provider_service
import ../../../app_service/service/profile/service as profile_service
import ../../../app_service/service/accounts/service as accounts_service
import ../../../app_service/service/accounts/utils as accounts_utils
import ../../../app_service/service/settings/service as settings_service
import ../../../app_service/service/contacts/service as contacts_service
import ../../../app_service/service/about/service as about_service
import ../../../app_service/service/language/service as language_service
import ../../../app_service/service/privacy/service as privacy_service
import ../../../app_service/service/stickers/service as stickers_service
import ../../../app_service/service/activity_center/service as activity_center_service
import ../../../app_service/service/saved_address/service as saved_address_service
import ../../../app_service/service/node/service as node_service
import ../../../app_service/service/node_configuration/service as node_configuration_service
import ../../../app_service/service/devices/service as devices_service
import ../../../app_service/service/mailservers/service as mailservers_service
import ../../../app_service/service/gif/service as gif_service
import ../../../app_service/service/ens/service as ens_service
import ../../../app_service/service/community_tokens/service as community_tokens_service
import ../../../app_service/service/network/service as network_service
import ../../../app_service/service/general/service as general_service
import ../../../app_service/service/keycard/service as keycard_service
import ../../../app_service/service/shared_urls/service as urls_service
import ../../../app_service/service/network_connection/service as network_connection_service
import ../../../app_service/service/visual_identity/service as procs_from_visual_identity_service
import ../../../app_service/common/types
import ../../../app_service/common/utils as common_utils
import app_service/service/keychain/service as keychain_service
import app_service/service/chat/service as chat_service
import app_service/service/community/service as community_service
import app_service/service/message/service as message_service
import app_service/service/token/service as token_service
import app_service/service/collectible/service as collectible_service
import app_service/service/currency/service as currency_service
import app_service/service/ramp/service as ramp_service
import app_service/service/transaction/service as transaction_service
import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/provider/service as provider_service
import app_service/service/profile/service as profile_service
import app_service/service/accounts/service as accounts_service
import app_service/service/accounts/utils as accounts_utils
import app_service/service/settings/service as settings_service
import app_service/service/contacts/service as contacts_service
import app_service/service/about/service as about_service
import app_service/service/language/service as language_service
import app_service/service/privacy/service as privacy_service
import app_service/service/stickers/service as stickers_service
import app_service/service/activity_center/service as activity_center_service
import app_service/service/saved_address/service as saved_address_service
import app_service/service/node/service as node_service
import app_service/service/node_configuration/service as node_configuration_service
import app_service/service/devices/service as devices_service
import app_service/service/mailservers/service as mailservers_service
import app_service/service/gif/service as gif_service
import app_service/service/ens/service as ens_service
import app_service/service/community_tokens/service as community_tokens_service
import app_service/service/network/service as network_service
import app_service/service/general/service as general_service
import app_service/service/keycard/service as keycard_service
import app_service/service/shared_urls/service as urls_service
import app_service/service/network_connection/service as network_connection_service
import app_service/service/visual_identity/service as procs_from_visual_identity_service
import app_service/common/types
import app_service/common/utils as common_utils
import app_service/service/network/network_item
import ../../core/notifications/details
import ../../core/eventemitter
import ../../core/custom_urls/urls_manager
import app/core/notifications/details
import app/core/eventemitter
import app/core/custom_urls/urls_manager
export io_interface
@ -101,6 +101,7 @@ type
walletAccountService: wallet_account_service.Service
keychainService: keychain_service.Service
networkConnectionService: network_connection_service.Service
stickersService: stickers_service.Service
walletSectionModule: wallet_section_module.AccessInterface
profileSectionModule: profile_section_module.AccessInterface
stickersModule: stickers_module.AccessInterface
@ -207,6 +208,7 @@ proc newModule*[T](
result.accountsService = accountsService
result.walletAccountService = walletAccountService
result.keychainService = keychainService
result.stickersService = stickersService
# Submodules
result.chatSectionModules = initOrderedTable[string, chat_section_module.AccessInterface]()
@ -334,7 +336,7 @@ proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityD
# If there are tokens already in the model, we should keep the existing community tokens, until
# getCommunityTokensDetailsAsync will trigger onCommunityTokensDetailsLoaded
if not existingCommunity.isEmpty() and not existingCommunity.communityTokens.isNil:
communityTokensItems = existingCommunity.communityTokens.items
communityTokensItems = existingCommunity.communityTokens.items
let (unviewedCount, notificationsCount) = self.controller.sectionUnreadMessagesAndMentionsCount(
communityDetails.id,
@ -436,6 +438,86 @@ proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityD
activeMembersCount = int(communityDetails.activeMembersCount),
)
proc sendNotification[T](self: Module[T], status: string, sendDetails: SendDetailsDto, sentTransaction: RouterSentTransaction) =
var
addressFrom = sendDetails.fromAddress
addressTo = sendDetails.toAddress
txTo = "" # txFrom is always the same as addressFrom, but txTo is different from addressTo when the app performs any sending flow via certain contract
fromChain = sendDetails.fromChain
toChain = sendDetails.toChain
fromAmount = sendDetails.fromAmount.toString(10)
toAmount = sendDetails.toAmount.toString(10)
fromAsset = sendDetails.fromToken
toAsset = sendDetails.toToken
error = ""
if not sendDetails.errorResponse.isNil:
error = sendDetails.errorResponse.details
if sentTransaction.hash.len > 0:
txTo = sentTransaction.toAddress
if sentTransaction.fromChain > 0:
fromChain = sentTransaction.fromChain
if sentTransaction.toChain > 0:
toChain = sentTransaction.toChain
let
txAmountIn = sentTransaction.amountIn.toString(10)
txAmountOut = sentTransaction.amountOut.toString(10)
if txAmountIn != "0":
fromAmount = txAmountIn
if txAmountOut != "0":
toAmount = txAmountOut
if sentTransaction.fromToken.len > 0:
fromAsset = sentTransaction.fromToken
if sentTransaction.toToken.len > 0:
toAsset = sentTransaction.toToken
var accFromName = ""
var accDto = self.walletAccountService.getAccountByAddress(addressFrom)
if not accDto.isNil:
accFromName = accDto.name
var accToName = ""
accDto = self.walletAccountService.getAccountByAddress(addressTo)
if not accDto.isNil:
accToName = accDto.name
var txToName = ""
let txType = SendType(sendDetails.sendType)
if txType == SendType.Bridge:
txToName = "Hop" # no translations, currently we hardcode providers here, when we add more, this info should come from the status-go side.
elif txType == SendType.Swap:
txToName = "ParaSwap" # no translations, currently we hardcode providers here, when we add more, this info should come from the status-go side.
else:
accDto = self.walletAccountService.getAccountByAddress(txTo)
if not accDto.isNil:
txToName = accDto.name
self.view.showTransactionToast(
sendDetails.uuid,
sendDetails.sendType,
fromChain,
toChain,
addressFrom,
accFromName,
addressTo,
accToName,
txTo,
txToName,
sentTransaction.hash,
sentTransaction.approvalTx,
fromAmount,
toAmount,
fromAsset,
toAsset,
sendDetails.username,
sendDetails.publicKey,
sendDetails.packId,
status,
error,
)
proc connectForNotificationsOnly[T](self: Module[T]) =
self.events.on(SIGNAL_WALLET_ACCOUNT_SAVED) do(e:Args):
let args = AccountArgs(e)
@ -464,22 +546,26 @@ proc connectForNotificationsOnly[T](self: Module[T]) =
kpName = args.keypairs[0].name
self.view.showToastKeypairsImported(kpName, args.keypairs.len, args.error)
self.events.on(SIGNAL_SENDING_TRANSACTIONS_STARTED) do(e:Args):
let args = TransactionArgs(e)
# we allow this notification only if an error occurs, otherwise skip it and display notification after the tx gets submitted to the network
if args.sendDetails.errorResponse.isNil:
return
self.sendNotification(args.status, args.sendDetails, args.sentTransaction)
self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args):
let args = TransactionSentArgs(e)
self.view.showToastTransactionSent(args.chainId, args.txHash, args.uuid, args.error,
ord(args.txType), args.fromAddress, args.toAddress, args.fromTokenKey, args.fromAmount,
args.toTokenKey, args.toAmount)
let args = TransactionArgs(e)
self.sendNotification(args.status, args.sendDetails, args.sentTransaction)
self.events.on(SIGNAL_TRANSACTION_STATUS_CHANGED) do(e:Args):
let args = TransactionArgs(e)
self.sendNotification(args.status, args.sendDetails, args.sentTransaction)
self.events.on(MARK_WALLET_ADDRESSES_AS_SHOWN) do(e:Args):
let args = WalletAddressesArgs(e)
for address in args.addresses:
self.addressWasShown(address)
self.events.on(SIGNAL_TRANSACTION_SENDING_COMPLETE) do(e:Args):
let args = TransactionMinedArgs(e)
self.view.showToastTransactionSendingComplete(args.chainId, args.transactionHash, args.data, args.success,
ord(args.txType), args.fromAddress, args.toAddress, args.fromTokenKey, args.fromAmount, args.toTokenKey, args.toAmount)
self.events.on(SIGNAL_PAIRING_FALLBACK_COMPLETED) do(e:Args):
self.view.showToastPairingFallbackCompleted()

View File

@ -339,12 +339,31 @@ QtObject:
proc showNetworkEndpointUpdated*(self: View, name: string, isTest: bool, revertedToDefault: bool) {.signal.}
proc showToastKeypairRemoved*(self: View, keypairName: string) {.signal.}
proc showToastKeypairsImported*(self: View, keypairName: string, keypairsCount: int, error: string) {.signal.}
proc showToastTransactionSent*(self: View, chainId: int, txHash: string, uuid: string, error: string,
txType: int, fromAddr: string, toAddr: string, fromTokenKey: string, fromAmount: string, toTokenKey: string, toAmount: string) {.signal.}
proc showToastTransactionSendingComplete*(self: View, chainId: int, txHash: string, data: string, success: bool,
txType: int, fromAddr: string, toAddr: string, fromTokenKey: string, fromAmount: string, toTokenKey: string, toAmount: string) {.signal.}
proc showToastPairingFallbackCompleted*(self: View) {.signal.}
proc showTransactionToast*(self: View,
uuid: string,
txType: int,
fromChainId: int,
toChainId: int,
fromAddr: string,
fromName: string,
toAddr: string,
toName: string,
txToAddr: string,
txToName: string,
txHash: string,
approvalTx: bool,
fromAmount: string,
toAmount: string,
fromAsset: string,
toAsset: string,
username: string,
publicKey: string,
packId: string,
status: string,
error: string) {.signal.}
## Used in test env only, for testing keycard flows
proc registerMockedKeycard*(self: View, cardIndex: int, readerState: int, keycardState: int,
mockedKeycard: string, mockedKeycardHelper: string) {.slot.} =

View File

@ -54,8 +54,14 @@ proc delete*(self: Controller) =
proc init*(self: Controller) =
self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args):
let args = TransactionSentArgs(e)
self.delegate.transactionWasSent(args.uuid, args.chainId, args.approvalTx, args.txHash, args.error)
let args = TransactionArgs(e)
self.delegate.transactionWasSent(
args.sendDetails.uuid,
args.sendDetails.fromChain,
args.sentTransaction.approvalTx,
args.sentTransaction.hash,
if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: ""
)
self.events.on(SIGNAL_OWNER_TOKEN_SENT) do(e:Args):
let args = OwnerTokenSentArgs(e)
@ -76,8 +82,8 @@ proc init*(self: Controller) =
self.delegate.prepareSignaturesForTransactions(data.data)
self.events.on(SIGNAL_TRANSACTION_STATUS_CHANGED) do(e:Args):
let args = TransactionStatusArgs(e)
self.delegate.transactionSendingComplete(args.data.hash, args.data.status)
let args = TransactionArgs(e)
self.delegate.transactionSendingComplete(args.sentTransaction.hash, args.status)
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
return self.walletAccountService.getWalletAccounts()

View File

@ -1,4 +1,6 @@
const
ETH_SYMBOL* = "ETH"
ETH_TRANSACTION_TYPE* = "eth"
ERC20_TRANSACTION_TYPE* = "erc20"
ERC721_TRANSACTION_TYPE* = "erc721"

View File

@ -146,48 +146,55 @@ QtObject:
proc doConnect(self: Service) =
self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args):
let args = TransactionSentArgs(e)
if args.txType != SendType.ENSRegister and args.txType != SendType.ENSSetPubKey and args.txType != SendType.ENSRelease:
let args = TransactionArgs(e)
let txType = SendType(args.sendDetails.sendType)
if txType != SendType.ENSRegister and txType != SendType.ENSSetPubKey and txType != SendType.ENSRelease:
return
var err = args.error
var err = if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: ""
var dto = EnsUsernameDto(
chainId: args.chainId,
username: args.username,
txHash: args.txHash,
txStatus: TxStatusPending
chainId: args.sendDetails.fromChain,
username: args.sendDetails.username,
txHash: args.sentTransaction.hash,
txStatus: args.status
)
if args.txType == SendType.ENSRegister:
if txType == SendType.ENSRegister:
dto.txType = RegisterENS
if err.len == 0:
let ensUsernameFinal = self.formatUsername(args.username, true)
if not self.add(args.chainId, ensUsernameFinal):
let ensUsernameFinal = self.formatUsername(args.sendDetails.username, true)
if not self.add(args.sendDetails.fromChain, ensUsernameFinal):
err = "failed to add ens username"
error "error", err
elif args.txType == SendType.ENSSetPubKey:
elif txType == SendType.ENSSetPubKey:
dto.txType = SetPubKey
if err.len == 0:
let usernameWithDomain = args.username.addDomain()
if not self.add(args.chainId, usernameWithDomain):
let usernameWithDomain = args.sendDetails.username.addDomain()
if not self.add(args.sendDetails.fromChain, usernameWithDomain):
err = "failed to set ens username"
error "error", err
elif args.txType == SendType.ENSRelease:
elif txType == SendType.ENSRelease:
dto.txType = ReleaseENS
if err.len == 0:
let ensUsernameFinal = self.formatUsername(args.username, true)
if not self.remove(args.chainId, ensUsernameFinal):
let ensUsernameFinal = self.formatUsername(args.sendDetails.username, true)
if not self.remove(args.sendDetails.fromChain, ensUsernameFinal):
err = "failed to remove ens username"
error "error", err
self.pendingEnsUsernames[makeKey(dto.username, args.chainId)] = dto
self.pendingEnsUsernames[makeKey(dto.username, args.sendDetails.fromChain)] = dto
let data = EnsTxResultArgs(transactionType: $dto.txType, chainId: args.chainId, ensUsername: args.username, txHash: args.txHash, error: err)
let data = EnsTxResultArgs(
transactionType: $dto.txType,
chainId: args.sendDetails.fromChain,
ensUsername: args.sendDetails.username,
txHash: args.sentTransaction.hash,
error: err
)
self.events.emit(SIGNAL_ENS_TRANSACTION_SENT, data)
self.events.on(SIGNAL_TRANSACTION_STATUS_CHANGED) do(e:Args):
let args = TransactionStatusArgs(e)
self.updateEnsUsernames(args.data.chainId, args.data.hash, args.data.status)
let args = TransactionArgs(e)
self.updateEnsUsernames(args.sentTransaction.fromChain, args.sentTransaction.hash, args.status)
proc init*(self: Service) =
self.doConnect()

View File

@ -153,17 +153,23 @@ QtObject:
proc init*(self: Service) =
self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args):
let args = TransactionSentArgs(e)
if args.txType != SendType.StickersBuy:
let args = TransactionArgs(e)
let txType = SendType(args.sendDetails.sendType)
if txType != SendType.StickersBuy:
return
self.marketStickerPacks[$args.packId].status = StickerPackStatus.Pending
self.marketStickerPacks[$args.packId].txHash = args.txHash
let data = StickerBuyResultArgs(chainId: args.chainId, packId: args.packId, txHash: args.txHash, error: args.error)
self.marketStickerPacks[$args.sendDetails.packId].status = StickerPackStatus.Pending
self.marketStickerPacks[$args.sendDetails.packId].txHash = args.sentTransaction.hash
let data = StickerBuyResultArgs(
chainId: args.sendDetails.fromChain,
packId: args.sendDetails.packId,
txHash: args.sentTransaction.hash,
error: if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: ""
)
self.events.emit(SIGNAL_STICKER_TRANSACTION_SENT, data)
self.events.on(SIGNAL_TRANSACTION_STATUS_CHANGED) do(e:Args):
let args = TransactionStatusArgs(e)
self.updateStickersPack(args.data.hash, args.data.status)
let args = TransactionArgs(e)
self.updateStickersPack(args.sentTransaction.hash, args.status)
proc setMarketStickerPacks*(self: Service, strickersJSON: string) {.slot.} =
let stickersResult = Json.decode(strickersJSON, tuple[packs: seq[StickerPackDto], error: string])
@ -222,6 +228,10 @@ QtObject:
return packId
return "0"
proc getStickersByPackId*(self: Service, packId: string): StickerPackDto =
if self.marketStickerPacks.hasKey(packId):
return self.marketStickerPacks[packId]
proc getRecentStickers*(self: Service): seq[StickerDto] =
try:
let recentResponse = status_stickers.recent()

View File

@ -3,16 +3,11 @@ import json, stint
include ../../common/json_utils
const
TxStatusSending* = "Sending"
TxStatusPending* = "Pending"
TxStatusSuccess* = "Success"
TxStatusFailed* = "Failed"
type
TransactionStatusChange* = ref object
status*: string
hash*: string
chainId*: int
type
ErrorResponse* = ref object
details*: string
@ -21,6 +16,8 @@ type
SendDetailsDto* = ref object
uuid*: string
sendType*: int
fromChain*: int
toChain*: int
fromAddress*: string
toAddress*: string
fromToken*: string
@ -54,7 +51,9 @@ type
toChain*: int
fromToken*: string
toToken*: string
amount*: UInt256 # amount of the transaction
amount*: UInt256 # amount sent
amountIn*: UInt256 # amount that is "data" of tx (important for erc20 tokens)
amountOut*: UInt256 # amount that will be received
hash*: string
approvalTx*: bool
@ -63,11 +62,13 @@ type
sendDetails*: SendDetailsDto
sentTransactions*: seq[RouterSentTransaction]
proc toTransactionStatusChange*(jsonObj: JsonNode): TransactionStatusChange =
result = TransactionStatusChange()
discard jsonObj.getProp("status", result.status)
discard jsonObj.getProp("hash", result.hash)
discard jsonObj.getProp("chainId", result.chainId)
type
TransactionStatusChange* = ref object
status*: string
hash*: string
chainId*: int
sendDetails*: SendDetailsDto
sentTransactions*: seq[RouterSentTransaction]
proc toErrorResponse*(jsonObj: JsonNode): ErrorResponse =
result = ErrorResponse()
@ -80,6 +81,8 @@ proc toSendDetailsDto*(jsonObj: JsonNode): SendDetailsDto =
result = SendDetailsDto()
discard jsonObj.getProp("uuid", result.uuid)
discard jsonObj.getProp("sendType", result.sendType)
discard jsonObj.getProp("fromChain", result.fromChain)
discard jsonObj.getProp("toChain", result.toChain)
discard jsonObj.getProp("fromAddress", result.fromAddress)
discard jsonObj.getProp("toAddress", result.toAddress)
discard jsonObj.getProp("fromToken", result.fromToken)
@ -130,10 +133,26 @@ proc toRouterSentTransaction*(jsonObj: JsonNode): RouterSentTransaction =
var tmpObj: JsonNode
if jsonObj.getProp("amount", tmpObj):
result.amount = stint.fromHex(UInt256, tmpObj.getStr)
if jsonObj.getProp("amountIn", tmpObj):
result.amountIn = stint.fromHex(UInt256, tmpObj.getStr)
if jsonObj.getProp("amountOut", tmpObj):
result.amountOut = stint.fromHex(UInt256, tmpObj.getStr)
proc toRouterSentTransactionsDto*(jsonObj: JsonNode): RouterSentTransactionsDto =
result = RouterSentTransactionsDto()
var tmpObj: JsonNode
if jsonObj.getProp("sendDetails", tmpObj) and tmpObj.kind == JObject:
result.sendDetails = toSendDetailsDto(tmpObj)
if jsonObj.getProp("sentTransactions", tmpObj) and tmpObj.kind == JArray:
for tx in tmpObj:
result.sentTransactions.add(toRouterSentTransaction(tx))
proc toTransactionStatusChange*(jsonObj: JsonNode): TransactionStatusChange =
result = TransactionStatusChange()
discard jsonObj.getProp("status", result.status)
discard jsonObj.getProp("hash", result.hash)
discard jsonObj.getProp("chainId", result.chainId)
var tmpObj: JsonNode
if jsonObj.getProp("sendDetails", tmpObj) and tmpObj.kind == JObject:
result.sendDetails = toSendDetailsDto(tmpObj)
if jsonObj.getProp("sentTransactions", tmpObj) and tmpObj.kind == JArray:

View File

@ -44,7 +44,6 @@ const SIGNAL_HISTORY_NON_ARCHIVAL_NODE* = "historyNonArchivalNode"
const SIGNAL_HISTORY_ERROR* = "historyError"
const SIGNAL_TRANSACTION_DECODED* = "transactionDecoded"
const SIGNAL_OWNER_TOKEN_SENT* = "ownerTokenSent"
const SIGNAL_TRANSACTION_SENDING_COMPLETE* = "transactionSendingComplete"
const SIGNAL_TRANSACTION_STATUS_CHANGED* = "transactionStatusChanged"
const InternalErrorCode = -1
@ -104,24 +103,6 @@ proc `$`*(self: TransactionMinedArgs): string =
except ValueError:
raiseAssert "static fmt"
type
TransactionSentArgs* = ref object of Args
chainId*: int
txHash*: string
uuid*: string
error*: string
txType*: SendType
fromAddress*: string
toAddress*: string
fromTokenKey*: string
fromAmount*: string
toTokenKey*: string
toAmount*: string
approvalTx*: bool
username*: string
publicKey*: string
packId*: string
type
OwnerTokenSentArgs* = ref object of Args
chainId*: int
@ -147,12 +128,10 @@ type
data*: RouterTransactionsForSigningDto
type
RouterTransactionsSendingStartedArgs* = ref object of Args
data*: SendDetailsDto
type
TransactionStatusArgs* = ref object of Args
data*: TransactionStatusChange
TransactionArgs* = ref object of Args
status*: string
sendDetails*: SendDetailsDto
sentTransaction*: RouterSentTransaction
QtObject:
type Service* = ref object of QObject
@ -201,9 +180,10 @@ QtObject:
self.events.on(SignalType.WalletRouterSendingTransactionsStarted.event) do(e:Args):
var data = WalletSignal(e)
self.events.emit(SIGNAL_SENDING_TRANSACTIONS_STARTED, RouterTransactionsSendingStartedArgs(data: data.routerTransactionsSendingDetails))
# TODO: the line below is to aling with the old implementation, remove it
self.sendTransactionsSignal(data.routerTransactionsSendingDetails)
self.events.emit(SIGNAL_SENDING_TRANSACTIONS_STARTED, TransactionArgs(
status: TxStatusSending,
sendDetails: data.routerTransactionsSendingDetails
))
self.events.on(SignalType.WalletRouterSignTransactions.event) do(e:Args):
var data = WalletSignal(e)
@ -219,18 +199,26 @@ QtObject:
self.events.on(SignalType.WalletTransactionStatusChanged.event) do(e:Args):
var data = WalletSignal(e)
self.events.emit(SIGNAL_TRANSACTION_STATUS_CHANGED, TransactionStatusArgs(data: data.transactionStatusChange))
if data.transactionStatusChange.isNil:
return
for tx in data.transactionStatusChange.sentTransactions:
self.events.emit(SIGNAL_TRANSACTION_STATUS_CHANGED, TransactionArgs(
status: data.transactionStatusChange.status,
sendDetails: data.transactionStatusChange.sendDetails,
sentTransaction: tx
))
self.events.on(PendingTransactionTypeDto.WalletTransfer.event) do(e: Args):
try:
var receivedData = TransactionMinedArgs(e)
let tokenMetadata = receivedData.data.parseJson().toTokenTransferMetadata()
if tokenMetadata.isOwnerToken:
let status = if receivedData.success: ContractTransactionStatus.Completed else: ContractTransactionStatus.Failed
self.events.emit(SIGNAL_OWNER_TOKEN_SENT, OwnerTokenSentArgs(chainId: receivedData.chainId, txHash: receivedData.transactionHash, tokenName: tokenMetadata.tokenName, status: status))
self.events.emit(SIGNAL_TRANSACTION_SENDING_COMPLETE, receivedData)
except Exception as e:
debug "Not the owner token transfer", msg=e.msg
# TODO: handle this for community related tx (minting, airdropping...)
# self.events.on(PendingTransactionTypeDto.WalletTransfer.event) do(e: Args):
# try:
# var receivedData = TransactionMinedArgs(e)
# let tokenMetadata = receivedData.data.parseJson().toTokenTransferMetadata()
# if tokenMetadata.isOwnerToken:
# let status = if receivedData.success: ContractTransactionStatus.Completed else: ContractTransactionStatus.Failed
# self.events.emit(SIGNAL_OWNER_TOKEN_SENT, OwnerTokenSentArgs(chainId: receivedData.chainId, txHash: receivedData.transactionHash, tokenName: tokenMetadata.tokenName, status: status))
# self.events.emit(SIGNAL_TRANSACTION_SENDING_COMPLETE, receivedData)
# except Exception as e:
# debug "Not the owner token transfer", msg=e.msg
proc getPendingTransactions*(self: Service): seq[TransactionDto] =
try:
@ -330,21 +318,8 @@ QtObject:
# that's why we need to mark the addresses as shown here (safer).
self.events.emit(MARK_WALLET_ADDRESSES_AS_SHOWN, WalletAddressesArgs(addresses: @[sendDetails.fromAddress, sendDetails.toAddress]))
if not sendDetails.errorResponse.isNil and sendDetails.errorResponse.details.len > 0:
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(
uuid: sendDetails.uuid,
error: sendDetails.errorResponse.details,
txType: SendType(sendDetails.sendType),
fromAddress: sendDetails.fromAddress,
toAddress: sendDetails.toAddress,
fromTokenKey: sendDetails.fromToken,
fromAmount: sendDetails.fromAmount.toString(10),
toTokenKey: sendDetails.toToken,
toAmount: sendDetails.toAmount.toString(10),
username: sendDetails.username,
publicKey: sendDetails.publicKey,
packId: sendDetails.packId
))
if sentTransactions.len == 0:
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionArgs(sendDetails: sendDetails))
return
for tx in sentTransactions:
@ -357,21 +332,10 @@ QtObject:
status: ContractTransactionStatus.InProgress
))
else:
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(
chainId: tx.fromChain,
txHash: tx.hash,
uuid: sendDetails.uuid,
txType: SendType(sendDetails.sendType),
fromAddress: tx.fromAddress,
toAddress: tx.toAddress,
fromTokenKey: tx.fromToken,
fromAmount: tx.amount.toString(10),
toTokenKey: tx.toToken,
toAmount: tx.amount.toString(10),
approvalTx: tx.approvalTx,
username: sendDetails.username,
publicKey: sendDetails.publicKey,
packId: sendDetails.packId
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionArgs(
status: TxStatusSending, # here should be TxStatusPending state, but that's not what Figma wants
sendDetails: sendDetails,
sentTransaction: tx
))
proc suggestedFees*(self: Service, chainId: int): SuggestedFeesDto =

View File

@ -435,49 +435,5 @@ Item {
}
}
Connections {
target: ensView.ensUsernamesStore.ensUsernamesModule
function onTransactionCompleted(success: bool, txHash: string, username: string, trxType: string) {
let title = ""
switch(trxType){
case d.registerENS:
title = !success ?
qsTr("ENS Registration failed")
:
qsTr("ENS Registration completed");
break;
case d.setPubKey:
title = !success ?
qsTr("Updating ENS pubkey failed")
:
qsTr("Updating ENS pubkey completed");
break;
case d.releaseENS:
title = !success ?
qsTr("Releasing ENS name failed")
:
qsTr("ENS name released");
break
default:
console.error("unknown transaction type: ", trxType);
return
}
let icon = "block-icon";
let ephType = Constants.ephemeralNotificationType.normal;
if (success) {
icon = "check-circle";
ephType = Constants.ephemeralNotificationType.success;
}
let url = `${ensView.ensUsernamesStore.getEtherscanTxLink()}/${txHash}`;
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on etherscan"),
icon,
false,
ephType,
url)
}
}
}

View File

@ -237,121 +237,276 @@ Item {
)
}
function onShowToastTransactionSent(chainId: int, txHash: string, uuid: string, error: string, txType: int,
fromAddr: string, toAddr: string, fromTokenKey: string, fromAmount: string,
toTokenKey: string, toAmount: string) {
switch(txType) {
case Constants.SendType.Approve: {
const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromTokenKey)
const fromAccountName = SQUtils.ModelUtils.getByKey(appMain.transactionStore.accounts, "address", fromAddr, "name")
const networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName")
if(!!fromToken && !!fromAccountName && !!networkName) {
const approvalAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals)
let toastTitle = qsTr("Setting spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName)
let toastSubtitle = qsTr("View on %1").arg(networkName)
let urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash)
let toastType = Constants.ephemeralNotificationType.normal
let icon = ""
if(error) {
toastTitle = qsTr("Failed to set spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName)
toastSubtitle = ""
urlLink = ""
toastType = Constants.ephemeralNotificationType.danger
icon = "warning"
}
Global.displayToastMessage(toastTitle, toastSubtitle, icon, !error, toastType, urlLink)
}
break
}
case Constants.SendType.Swap: {
const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromTokenKey)
const toToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", toTokenKey)
const fromAccountName = SQUtils.ModelUtils.getByKey(appMain.transactionStore.accounts, "address", fromAddr, "name")
const networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName")
if(!!fromToken && !!toToken && !!fromAccountName && !!networkName) {
const fromSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals)
const toSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(toAmount, toToken.symbol, toToken.decimals)
let toastTitle = qsTr("Swapping %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName)
let toastSubtitle = qsTr("View on %1").arg(networkName)
let urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash)
let toastType = Constants.ephemeralNotificationType.normal
let icon = ""
if(error) {
toastTitle = qsTr("Failed to swap %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName)
toastSubtitle = ""
urlLink = ""
toastType = Constants.ephemeralNotificationType.danger
icon = "warning"
}
Global.displayToastMessage(toastTitle, toastSubtitle, icon, !error, toastType, urlLink)
}
break
}
default: {
if (!error) {
let networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName")
if(!!networkName) {
Global.displayToastMessage(qsTr("Transaction pending..."),
qsTr("View on %1").arg(networkName),
"",
true,
Constants.ephemeralNotificationType.normal,
"%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash))
}
}
break
}
}
}
function onShowTransactionToast(uuid: string,
txType: int,
fromChainId: int,
toChainId: int,
fromAddr: string,
fromName: string,
toAddr: string,
toName: string,
txToAddr: string,
txToName: string,
txHash: string,
approvalTx: bool,
fromAmount: string,
toAmount: string,
fromAsset: string,
toAsset: string,
username: string,
publicKey: string,
packId: string,
status: string,
error: string) {
function onShowToastTransactionSendingComplete(chainId: int, txHash: string, data: string, success: bool,
txType: int, fromAddr: string, toAddr: string, fromTokenKey: string,
fromAmount: string, toTokenKey: string, toAmount: string) {
switch(txType) {
case Constants.SendType.Approve: {
const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromTokenKey)
const fromAccountName = SQUtils.ModelUtils.getByKey(appMain.transactionStore.accounts, "address", fromAddr, "name")
const networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName")
if(!!fromToken && !!fromAccountName && !!networkName) {
const approvalAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals)
let toastTitle = qsTr("Spending cap set: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName)
const toastSubtitle = qsTr("View on %1").arg(networkName)
const urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash)
let toastType = Constants.ephemeralNotificationType.success
let icon = "checkmark-circle"
if(!success) {
toastTitle = qsTr("Failed to set spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName)
toastType = Constants.ephemeralNotificationType.danger
icon = "warning"
let toastTitle = ""
let toastSubtitle = ""
let toastIcon = ""
let toastLoading = false
let toastType = Constants.ephemeralNotificationType.normal
let toastLink = ""
const sender = !!fromName? fromName : SQUtils.Utils.elideText(fromAddr, 6, 4)
let senderChainName = qsTr("unknown")
let sentAmount = ""
const recipient = !!toName? toName : SQUtils.Utils.elideText(toAddr, 6, 4)
const txRecipient = !!txToName? txToName : SQUtils.Utils.elideText(txToAddr, 6, 4)
let recipientChainName = qsTr("unknown")
let receivedAmount = ""
let assetName = qsTr("unknown")
let ensName = d.ensName(username)
let stickersPackName = qsTr("unknown")
if (!!txHash) {
toastLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(fromChainId)).arg(txHash)
}
const fromChainName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", fromChainId, "chainName")
if (!!fromChainName) {
senderChainName = fromChainName
toastSubtitle = qsTr("View on %1").arg(senderChainName)
}
const toChainName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", toChainId, "chainName")
if (!!toChainName) {
recipientChainName = toChainName
}
const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromAsset)
if (!!fromToken) {
sentAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals)
}
const toToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", toAsset)
if (!!toToken) {
receivedAmount = currencyStore.formatCurrencyAmountFromBigInt(toAmount, toToken.symbol, toToken.decimals)
}
if (txType === Constants.SendType.ERC721Transfer || txType === Constants.SendType.ERC1155Transfer) {
const key = "%1+%2+%3".arg(fromChainId).arg(txToAddr).arg(fromAsset)
const entry = SQUtils.ModelUtils.getByKey(appMain.walletCollectiblesStore.allCollectiblesModel, "symbol", key)
if (!!entry) {
assetName = entry.name
}
}
if (txType === Constants.SendType.StickersBuy) {
const idx = appMain.rootChatStore.stickersModuleInst.stickerPacks.findIndexById(packId, false)
if(idx >= 0) {
const entry = SQUtils.ModelUtils.get(appMain.rootChatStore.stickersModuleInst.stickerPacks, idx)
if (!!entry) {
stickersPackName = entry.name
}
Global.displayToastMessage(toastTitle, toastSubtitle, icon, false, toastType, urlLink)
}
}
switch(status) {
case Constants.txStatus.sending: {
toastTitle = qsTr("Sending %1 from %2 to %3")
toastLoading = true
switch(txType) {
case Constants.SendType.Transfer: {
toastTitle = toastTitle.arg(sentAmount).arg(sender).arg(recipient)
break
}
case Constants.SendType.ENSRegister: {
toastTitle = qsTr("Registering %1 ENS name using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.ENSRelease: {
toastTitle = qsTr("Releasing %1 ENS username using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.ENSSetPubKey: {
toastTitle = qsTr("Setting public key %1 using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.StickersBuy: {
toastTitle = qsTr("Purchasing %1 sticker pack using %2").arg(stickersPackName).arg(sender)
break
}
case Constants.SendType.Bridge: {
toastTitle = qsTr("Bridging %1 from %2 to %3 in %4").arg(sentAmount).arg(senderChainName).arg(recipientChainName).arg(sender)
if (approvalTx) {
toastTitle = qsTr("Setting spending cap: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient)
}
break
}
case Constants.SendType.ERC721Transfer: {
toastTitle = toastTitle.arg(assetName).arg(sender).arg(recipient)
break
}
case Constants.SendType.ERC1155Transfer: {
toastTitle = qsTr("Sending %1 %2 from %3 to %4").arg(fromAmount).arg(assetName).arg(sender).arg(recipient)
break
}
case Constants.SendType.Swap: {
toastTitle = qsTr("Swapping %1 to %2 in %3").arg(sentAmount).arg(receivedAmount).arg(sender)
if (approvalTx) {
toastTitle = qsTr("Setting spending cap: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient)
}
break
}
case Constants.SendType.Approve: {
console.warn("tx type approve not yet identified as a stand alone path")
break
}
default:
console.warn("status: sending - tx type not supproted")
return
}
break
}
case Constants.SendType.Swap: {
const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromTokenKey)
const toToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", toTokenKey)
const fromAccountName = SQUtils.ModelUtils.getByKey(appMain.transactionStore.accounts, "address", fromAddr, "name")
const networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName")
if(!!fromToken && !!toToken && !!fromAccountName && !!networkName) {
const fromSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals)
const toSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(toAmount, toToken.symbol, toToken.decimals)
let toastTitle = qsTr("Swapped %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName)
const toastSubtitle = qsTr("View on %1").arg(networkName)
const urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash)
let toastType = Constants.ephemeralNotificationType.success
let icon = "checkmark-circle"
if(!success) {
toastTitle = qsTr("Failed to swap %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName)
toastType = Constants.ephemeralNotificationType.danger
icon = "warning"
case Constants.txStatus.pending: {
// So far we don't display notification when it's accepted by the network and its status is pending
// discussed in wallet group chat, we considered that pending status will be displayed almost at the
// same time as sending and decided to skip it.
return
}
case Constants.txStatus.success: {
toastTitle = qsTr("Sent %1 from %2 to %3")
toastIcon = "checkmark-circle"
toastType = Constants.ephemeralNotificationType.success
switch(txType) {
case Constants.SendType.Transfer: {
toastTitle = toastTitle.arg(sentAmount).arg(sender).arg(recipient)
break
}
case Constants.SendType.ENSRegister: {
toastTitle = qsTr("Registered %1 ENS name using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.ENSRelease: {
toastTitle = qsTr("Released %1 ENS username using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.ENSSetPubKey: {
toastTitle = qsTr("Set public key %1 using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.StickersBuy: {
toastTitle = qsTr("Purchased %1 sticker pack using %2").arg(stickersPackName).arg(sender)
break
}
case Constants.SendType.Bridge: {
toastTitle = qsTr("Bridged %1 from %2 to %3 in %4").arg(sentAmount).arg(senderChainName).arg(recipientChainName).arg(sender)
if (approvalTx) {
toastTitle = qsTr("Spending spending cap: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient)
}
Global.displayToastMessage(toastTitle, toastSubtitle, icon, false, toastType, urlLink)
break
}
case Constants.SendType.ERC721Transfer: {
toastTitle = toastTitle.arg(assetName).arg(sender).arg(recipient)
break
}
case Constants.SendType.ERC1155Transfer: {
toastTitle = qsTr("Sent %1 %2 from %3 to %4").arg(fromAmount).arg(assetName).arg(sender).arg(recipient)
break
}
case Constants.SendType.Swap: {
toastTitle = qsTr("Swapped %1 to %2 in %3").arg(sentAmount).arg(receivedAmount).arg(sender)
if (approvalTx) {
toastTitle = qsTr("Spending cap set: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient)
}
break
}
case Constants.SendType.Approve: {
console.warn("tx type approve not yet identified as a stand alone path")
break
}
default:
console.warn("status: success - tx type not supproted")
return
}
break
}
default: break
case Constants.txStatus.failed: {
toastTitle = "Send failed: %1 from %2 to %3"
toastIcon = "warning"
toastType = Constants.ephemeralNotificationType.danger
switch(txType) {
case Constants.SendType.Transfer: {
toastTitle = toastTitle.arg(sentAmount).arg(sender).arg(recipient)
break
}
case Constants.SendType.ENSRegister: {
toastTitle = qsTr("ENS username registeration failed: %1 using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.ENSRelease: {
toastTitle = qsTr("ENS username release failed: %1 using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.ENSSetPubKey: {
toastTitle = qsTr("Set public key failed: %1 using %2").arg(ensName).arg(sender)
break
}
case Constants.SendType.StickersBuy: {
toastTitle = qsTr("Sticker pack purchase failed: %1 using %2").arg(stickersPackName).arg(sender)
break
}
case Constants.SendType.Bridge: {
toastTitle = qsTr("Bridge failed: %1 from %2 to %3 in %4").arg(sentAmount).arg(senderChainName).arg(recipientChainName).arg(sender)
if (approvalTx) {
toastTitle = qsTr("Spending spending failed: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient)
}
break
}
case Constants.SendType.ERC721Transfer: {
toastTitle = toastTitle.arg(assetName).arg(sender).arg(recipient)
break
}
case Constants.SendType.ERC1155Transfer: {
toastTitle = qsTr("Send failed: %1 %2 from %3 to %4").arg(fromAmount).arg(assetName).arg(sender).arg(recipient)
break
}
case Constants.SendType.Swap: {
toastTitle = qsTr("Swapp failed: %1 to %2 in %3").arg(sentAmount).arg(receivedAmount).arg(sender)
if (approvalTx) {
toastTitle = qsTr("Spending cap failed: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient)
}
break
}
case Constants.SendType.Approve: {
console.warn("tx type approve not yet identified as a stand alone path")
break
}
default:
console.warn("status: failed - tx type not supproted")
return
}
break
}
default:
console.warn("not supported status")
return
}
Global.displayToastMessage(toastTitle, toastSubtitle, toastIcon, toastLoading, toastType, toastLink)
}
function onCommunityMemberStatusEphemeralNotification(communityName: string, memberName: string, state: CommunityMembershipRequestState) {
@ -408,6 +563,13 @@ Item {
activityCenterPopupObj.open()
}
}
function ensName(username) {
if (!username.endsWith(".stateofus.eth") && !username.endsWith(".eth")) {
return "%1.%2".arg(username).arg("stateofus.eth")
}
return username
}
}
Settings {

View File

@ -1104,6 +1104,13 @@ QtObject {
High
}
readonly property QtObject txStatus: QtObject {
readonly property string sending: "Sending"
readonly property string pending: "Pending"
readonly property string success: "Success"
readonly property string failed: "Failed"
}
enum LoginType {
Password,
Biometrics,