feat: remove all remaining spawnAndSends

refactor: move threadpool task declarations inline with views

Co-authored-by: Michael Bradley Jr. <michaelsbradleyjr@gmail.com>
Co-authored-by: Eric Mastro <eric.mastro@gmail.com>
This commit is contained in:
Michael Bradley, Jr 2021-03-18 19:23:15 -05:00 committed by Iuri Matias
parent 686cbc7f54
commit 1200632989
19 changed files with 551 additions and 384 deletions

View File

@ -14,18 +14,87 @@ import ../../status/ens as status_ens
import ../../status/chat/[chat, message]
import ../../status/profile/profile
import web3/[conversions, ethtypes]
import ../../status/threads
import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions, communities, community_list, community_item]
import json_serialization
import ../utils/image_utils
import ../../status/tasks/[qt, task_runner_impl]
logScope:
topics = "chats-view"
type
ChatViewRoles {.pure.} = enum
MessageList = UserRole + 1
GetLinkPreviewDataTaskArg = ref object of QObjectTaskArg
link: string
uuid: string
AsyncMessageLoadTaskArg = ref object of QObjectTaskArg
chatId: string
ResolveEnsTaskArg = ref object of QObjectTaskArg
ens: string
const getLinkPreviewDataTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[GetLinkPreviewDataTaskArg](argEncoded)
var success: bool
# We need to call directly on libstatus because going through the status model is not thread safe
let
response = libstatus_chat.getLinkPreviewData(arg.link, success)
responseJson = %* { "result": %response, "success": %success, "uuid": %arg.uuid }
arg.finish(responseJson)
proc getLinkPreviewData[T](self: T, slot: string, link: string, uuid: string) =
let arg = GetLinkPreviewDataTaskArg(
tptr: cast[ByteAddress](getLinkPreviewDataTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
link: link,
uuid: uuid
)
self.status.tasks.threadpool.start(arg)
const asyncMessageLoadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncMessageLoadTaskArg](argEncoded)
var messages: JsonNode
var msgCallSuccess: bool
let msgCallResult = rpcChatMessages(arg.chatId, newJString(""), 20, msgCallSuccess)
if(msgCallSuccess):
messages = msgCallResult.parseJson()["result"]
var reactions: JsonNode
var reactionsCallSuccess: bool
let reactionsCallResult = rpcReactions(arg.chatId, newJString(""), 20, reactionsCallSuccess)
if(reactionsCallSuccess):
reactions = reactionsCallResult.parseJson()["result"]
let responseJson = %*{
"chatId": arg.chatId,
"messages": messages,
"reactions": reactions
}
arg.finish(responseJson)
proc asyncMessageLoad[T](self: T, slot: string, chatId: string) =
let arg = AsyncMessageLoadTaskArg(
tptr: cast[ByteAddress](asyncMessageLoadTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
chatId: chatId
)
self.status.tasks.threadpool.start(arg)
const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[ResolveEnsTaskArg](argEncoded)
result = status_ens.pubkey(arg.ens)
arg.finish(result)
proc resolveEns[T](self: T, slot: string, ens: string) =
let arg = ResolveEnsTaskArg(
tptr: cast[ByteAddress](asyncMessageLoadTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
ens: ens
)
self.status.tasks.threadpool.start(arg)
QtObject:
type
@ -487,11 +556,7 @@ QtObject:
self.linkPreviewDataWasReceived(previewData)
proc getLinkPreviewData*(self: ChatsView, link: string, uuid: string) {.slot.} =
spawnAndSend(self, "linkPreviewDataReceived") do:
var success: bool
# We need to call directly on libstatus because going through the status model is not thread safe
let response = libstatus_chat.getLinkPreviewData(link, success)
$(%* { "result": %response, "success": %success, "uuid": %uuid })
self.getLinkPreviewData("linkPreviewDataReceived", link, uuid)
proc joinChat*(self: ChatsView, channel: string, chatTypeInt: int): int {.slot.} =
self.status.chat.join(channel, ChatType(chatTypeInt))
@ -524,26 +589,7 @@ QtObject:
proc loadingMessagesChanged*(self: ChatsView, value: bool) {.signal.}
proc asyncMessageLoad*(self: ChatsView, chatId: string) {.slot.} =
spawnAndSend(self, "asyncMessageLoaded") do: # Call self.ensResolved(string) when ens is resolved
var messages: JsonNode
var msgCallSuccess: bool
let msgCallResult = rpcChatMessages(chatId, newJString(""), 20, msgCallSuccess)
if(msgCallSuccess):
messages = msgCallResult.parseJson()["result"]
var reactions: JsonNode
var reactionsCallSuccess: bool
let reactionsCallResult = rpcReactions(chatId, newJString(""), 20, reactionsCallSuccess)
if(reactionsCallSuccess):
reactions = reactionsCallResult.parseJson()["result"]
$(%*{
"chatId": chatId,
"messages": messages,
"reactions": reactions
})
self.asyncMessageLoad("asyncMessageLoaded", chatId)
proc asyncMessageLoaded*(self: ChatsView, rpcResponse: string) {.slot.} =
let rpcResponseObj = rpcResponse.parseJson
@ -677,8 +723,7 @@ QtObject:
# Resolving a ENS name
proc resolveENS*(self: ChatsView, ens: string) {.slot.} =
spawnAndSend(self, "ensResolved") do: # Call self.ensResolved(string) when ens is resolved
status_ens.pubkey(ens)
self.resolveEns("ensResolved", ens) # Call self.ensResolved(string) when ens is resolved
proc ensWasResolved*(self: ChatsView, resolvedPubKey: string) {.signal.}

View File

@ -1,15 +1,67 @@
import NimQml, tables, json, chronicles, sets, strutils
import ../../../status/[status, stickers, threads]
import ../../../status/[status, stickers]
import ../../../status/libstatus/[types, utils]
import ../../../status/libstatus/stickers as status_stickers
import ../../../status/libstatus/wallet as status_wallet
import sticker_pack_list, sticker_list, chat_item
import json_serialization
import ../../../status/tasks/task_manager
import ../../../status/tasks/[qt, task_runner_impl]
logScope:
topics = "stickers-view"
type
EstimateTaskArg = ref object of QObjectTaskArg
packId: int
address: string
price: string
uuid: string
ObtainAvailableStickerPacksTaskArg = ref object of QObjectTaskArg
# The pragmas `{.gcsafe, nimcall.}` in this context do not force the compiler
# to accept unsafe code, rather they work in conjunction with the proc
# signature for `type Task` in tasks/common.nim to ensure that the proc really
# is gcsafe and that a helpful error message is displayed
const estimateTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[EstimateTaskArg](argEncoded)
var success: bool
var estimate = estimateGas(arg.packId, arg.address, arg.price,
success)
if not success:
estimate = 325000
let tpl: tuple[estimate: int, uuid: string] = (estimate, arg.uuid)
arg.finish(tpl)
# the [T] here is annoying but the QtObject template only allows for one type
# definition so we'll need to setup the type, task, and helper outside of body
# passed to `QtObject:`
proc estimate[T](self: T, slot: string, packId: int, address: string, price: string,
uuid: string) =
let arg = EstimateTaskArg(
tptr: cast[ByteAddress](estimateTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
packId: packId,
address: address,
price: price,
uuid: uuid
)
self.status.tasks.threadpool.start(arg)
const obtainAvailableStickerPacksTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[ObtainAvailableStickerPacksTaskArg](argEncoded)
let availableStickerPacks = status_stickers.getAvailableStickerPacks()
var packs: seq[StickerPack] = @[]
for packId, stickerPack in availableStickerPacks.pairs:
packs.add(stickerPack)
arg.finish(%*(packs))
proc obtainAvailableStickerPacks[T](self: T, slot: string) =
let arg = ObtainAvailableStickerPacksTaskArg(
tptr: cast[ByteAddress](obtainAvailableStickerPacksTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot)
self.status.tasks.threadpool.start(arg)
QtObject:
type StickersView* = ref object of QObject
status: Status
@ -46,7 +98,7 @@ QtObject:
proc transactionCompleted*(self: StickersView, success: bool, txHash: string, revertReason: string = "") {.signal.}
proc estimate*(self: StickersView, packId: int, address: string, price: string, uuid: string) {.slot.} =
self.status.taskManager.threadPool.stickers.stickerPackPurchaseGasEstimate(cast[pointer](self.vptr), "setGasEstimate", packId, address, price, uuid)
self.estimate("setGasEstimate", packId, address, price, uuid)
proc gasEstimateReturned*(self: StickersView, estimate: int, uuid: string) {.signal.}
@ -65,7 +117,7 @@ QtObject:
self.transactionWasSent(response)
proc obtainAvailableStickerPacks*(self: StickersView) =
self.status.taskManager.threadPool.stickers.obtainAvailableStickerPacks(cast[pointer](self.vptr), "setAvailableStickerPacks")
self.obtainAvailableStickerPacks("setAvailableStickerPacks")
proc stickerPacksLoaded*(self: StickersView) {.signal.}

View File

@ -10,7 +10,6 @@ import ../../status/libstatus/settings as status_settings
import ../../status/status
import ../../status/ens as status_ens
import ../../status/chat/chat
import ../../status/threads
import ../../status/libstatus/types
import ../../status/libstatus/accounts/constants as accountConstants
import qrcode/qrcode

View File

@ -1,15 +1,35 @@
import NimQml, chronicles, sequtils, sugar, strutils
import ../../../status/libstatus/utils as status_utils
import ../../../status/status
import ../../../status/threads
import ../../../status/chat/chat
import contact_list
import ../../../status/profile/profile
import ../../../status/ens as status_ens
import ../../../status/tasks/[qt, task_runner_impl]
logScope:
topics = "contacts-view"
type
LookupContactTaskArg = ref object of QObjectTaskArg
value: string
const lookupContactTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[LookupContactTaskArg](argEncoded)
var id = arg.value
if not id.startsWith("0x"):
id = status_ens.pubkey(id)
arg.finish(id)
proc lookupContact[T](self: T, slot: string, value: string) =
let arg = LookupContactTaskArg(
tptr: cast[ByteAddress](lookupContactTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
value: value
)
self.status.tasks.threadpool.start(arg)
QtObject:
type ContactsView* = ref object of QObject
status: Status
@ -113,11 +133,7 @@ QtObject:
if value == "":
return
spawnAndSend(self, "ensResolved") do: # Call self.ensResolved(string) when ens is resolved
var id = value
if not id.startsWith("0x"):
id = status_ens.pubkey(id)
id
self.lookupContact("ensResolved", value)
proc ensWasResolved*(self: ContactsView, resolvedPubKey: string) {.signal.}

View File

@ -1,11 +1,9 @@
import NimQml
import Tables
import json
import json_serialization
import sequtils
import strutils
from ../../../status/libstatus/types import Setting, PendingTransactionType, RpcException
import ../../../status/threads
import ../../../status/ens as status_ens
import ../../../status/libstatus/wallet as status_wallet
import ../../../status/libstatus/settings as status_settings
@ -14,11 +12,78 @@ import ../../../status/status
import ../../../status/wallet
import sets
import web3/ethtypes
import ../../../status/tasks/[qt, task_runner_impl]
type
EnsRoles {.pure.} = enum
UserName = UserRole + 1
IsPending = UserRole + 2
ValidateTaskArg = ref object of QObjectTaskArg
ens: string
isStatus: bool
usernames: seq[string]
DetailsTaskArg = ref object of QObjectTaskArg
username: string
const validateTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[ValidateTaskArg](argEncoded)
username = arg.ens & (if(arg.isStatus): status_ens.domain else: "")
var output = ""
if arg.usernames.filter(proc(x: string):bool = x == username).len > 0:
output = "already-connected"
else:
let ownerAddr = status_ens.owner(username)
if ownerAddr == "" and arg.isStatus:
output = "available"
else:
let userPubKey = status_settings.getSetting[string](Setting.PublicKey, "0x0")
let userWallet = status_wallet.getWalletAccounts()[0].address
let pubkey = status_ens.pubkey(arg.ens)
if ownerAddr != "":
if pubkey == "" and ownerAddr == userWallet:
output = "owned" # "Continuing will connect this username with your chat key."
elif pubkey == userPubkey:
output = "connected"
elif ownerAddr == userWallet:
output = "connected-different-key" # "Continuing will require a transaction to connect the username with your current chat key.",
else:
output = "taken"
else:
output = "taken"
arg.finish(output)
proc validate[T](self: T, slot: string, ens: string, isStatus: bool, usernames: seq[string]) =
let arg = ValidateTaskArg(
tptr: cast[ByteAddress](validateTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
ens: ens,
isStatus: isStatus,
usernames: usernames
)
self.status.tasks.threadpool.start(arg)
const detailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[DetailsTaskArg](argEncoded)
address = status_ens.address(arg.username)
pubkey = status_ens.pubkey(arg.username)
json = %* {
"ensName": arg.username,
"address": address,
"pubkey": pubkey
}
arg.finish(json)
proc details[T](self: T, slot: string, username: string) =
let arg = DetailsTaskArg(
tptr: cast[ByteAddress](detailsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
username: username
)
self.status.tasks.threadpool.start(arg)
QtObject:
type EnsManager* = ref object of QAbstractListModel
@ -58,31 +123,7 @@ QtObject:
self.ensWasResolved(ensResult)
proc validate*(self: EnsManager, ens: string, isStatus: bool) {.slot.} =
let username = ens & (if(isStatus): status_ens.domain else: "")
if self.usernames.filter(proc(x: string):bool = x == username).len > 0:
self.ensResolved("already-connected")
else:
spawnAndSend(self, "ensResolved") do:
let ownerAddr = status_ens.owner(username)
var output = ""
if ownerAddr == "" and isStatus:
output = "available"
else:
let userPubKey = status_settings.getSetting[string](Setting.PublicKey, "0x0")
let userWallet = status_wallet.getWalletAccounts()[0].address
let pubkey = status_ens.pubkey(ens)
if ownerAddr != "":
if pubkey == "" and ownerAddr == userWallet:
output = "owned" # "Continuing will connect this username with your chat key."
elif pubkey == userPubkey:
output = "connected"
elif ownerAddr == userWallet:
output = "connected-different-key" # "Continuing will require a transaction to connect the username with your current chat key.",
else:
output = "taken"
else:
output = "taken"
output
self.validate("ensResolved", ens, isStatus, self.usernames)
proc add*(self: EnsManager, username: string) =
self.beginInsertRows(newQModelIndex(), self.usernames.len, self.usernames.len)
@ -119,14 +160,7 @@ QtObject:
proc details(self: EnsManager, username: string) {.slot.} =
self.loading(true)
spawnAndSend(self, "setDetails") do:
let address = status_ens.address(username)
let pubkey = status_ens.pubkey(username)
$(%* {
"ensName": username,
"address": address,
"pubkey": pubkey
})
self.details("setDetails", username)
proc detailsObtained(self: EnsManager, ensName: string, address: string, pubkey: string) {.signal.}

View File

@ -129,7 +129,7 @@ QtObject:
var success: bool
# TODO make this async
let response = status.wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, password, success, txData)
let response = wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, password, success, txData)
let errorMessage = if not success:
if response == "":
"web3-response-error"

View File

@ -71,7 +71,7 @@ proc init*(self: WalletController) =
self.view.transactionCompleted(tx.success, tx.transactionHash, tx.revertReason)
proc checkPendingTransactions*(self: WalletController) =
self.status.wallet.checkPendingTransactions() # TODO: consider doing this in a spawnAndSend
self.status.wallet.checkPendingTransactions() # TODO: consider doing this in a threadpool task
proc start*(self: WalletController) =
status_wallet.startWallet(false)

View File

@ -1,5 +1,5 @@
import NimQml, Tables, strformat, strutils, chronicles, sequtils, json, std/wrapnils, parseUtils, stint, tables, json_serialization
import ../../status/[status, wallet, threads]
import NimQml, Tables, strformat, strutils, chronicles, sequtils, json, std/wrapnils, parseUtils, stint, tables
import ../../status/[status, wallet]
import ../../status/wallet/collectibles as status_collectibles
import ../../status/libstatus/accounts/constants
import ../../status/libstatus/wallet as status_wallet
@ -10,6 +10,160 @@ import ../../status/libstatus/utils as status_utils
import ../../status/libstatus/eth/contracts
import ../../status/ens as status_ens
import views/[asset_list, account_list, account_item, token_list, transaction_list, collectibles_list]
import ../../status/tasks/[qt, task_runner_impl]
type
SendTransactionTaskArg = ref object of QObjectTaskArg
from_addr: string
to: string
assetAddress: string
value: string
gas: string
gasPrice: string
password: string
uuid: string
InitBalancesTaskArg = ref object of QObjectTaskArg
address: string
tokenList: seq[string]
LoadCollectiblesTaskArg = ref object of QObjectTaskArg
address: string
collectiblesType: string
GasPredictionsTaskArg = ref object of QObjectTaskArg
LoadTransactionsTaskArg = ref object of QObjectTaskArg
address: string
blockNumber: string
ResolveEnsTaskArg = ref object of QObjectTaskArg
ens: string
uuid: string
const sendTransactionTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[SendTransactionTaskArg](argEncoded)
var
success: bool
response: string
if arg.assetAddress != ZERO_ADDRESS and not arg.assetAddress.isEmptyOrWhitespace:
response = wallet.sendTokenTransaction(arg.from_addr, arg.to, arg.assetAddress, arg.value, arg.gas, arg.gasPrice, arg.password, success)
else:
response = wallet.sendTransaction(arg.from_addr, arg.to, arg.value, arg.gas, arg.gasPrice, arg.password, success)
let output = %* { "result": %response, "success": %success, "uuid": %arg.uuid }
arg.finish(output)
proc sendTransaction[T](self: T, slot: string, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) =
let arg = SendTransactionTaskArg(
tptr: cast[ByteAddress](sendTransactionTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
from_addr: from_addr,
to: to,
assetAddress: assetAddress,
value: value,
gas: gas,
gasPrice: gasPrice,
password: password,
uuid: uuid
)
self.status.tasks.threadpool.start(arg)
const initBalancesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[InitBalancesTaskArg](argEncoded)
var tokenBalances = initTable[string, string]()
for token in arg.tokenList:
tokenBalances[token] = getTokenBalance(token, arg.address)
let output = %* {
"address": arg.address,
"eth": getEthBalance(arg.address),
"tokens": tokenBalances
}
arg.finish(output)
proc initBalances[T](self: T, slot: string, address: string, tokenList: seq[string]) =
let arg = InitBalancesTaskArg(
tptr: cast[ByteAddress](initBalancesTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
address: address,
tokenList: tokenList
)
self.status.tasks.threadpool.start(arg)
const loadCollectiblesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[LoadCollectiblesTaskArg](argEncoded)
var collectiblesOrError = ""
case arg.collectiblesType:
of status_collectibles.CRYPTOKITTY:
collectiblesOrError = status_collectibles.getCryptoKitties(arg.address)
of status_collectibles.KUDO:
collectiblesOrError = status_collectibles.getKudos(arg.address)
of status_collectibles.ETHERMON:
collectiblesOrError = status_collectibles.getEthermons(arg.address)
of status_collectibles.STICKER:
collectiblesOrError = status_collectibles.getStickers(arg.address)
let output = %*{
"address": arg.address,
"collectibleType": arg.collectiblesType,
"collectiblesOrError": collectiblesOrError
}
arg.finish(output)
proc loadCollectibles[T](self: T, slot: string, address: string, collectiblesType: string) =
let arg = LoadCollectiblesTaskArg(
tptr: cast[ByteAddress](loadCollectiblesTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
address: address,
collectiblesType: collectiblesType,
)
self.status.tasks.threadpool.start(arg)
const getGasPredictionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[GasPredictionsTaskArg](argEncoded)
output = %getGasPricePredictions()
arg.finish(output)
proc getGasPredictions[T](self: T, slot: string) =
let arg = GasPredictionsTaskArg(
tptr: cast[ByteAddress](getGasPredictionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot
)
self.status.tasks.threadpool.start(arg)
const loadTransactionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[LoadTransactionsTaskArg](argEncoded)
output = %*{
"address": arg.address,
"history": getTransfersByAddress(arg.address, arg.blockNumber)
}
arg.finish(output)
proc loadTransactions[T](self: T, slot: string, address: string, blockNumber: string) =
let arg = LoadTransactionsTaskArg(
tptr: cast[ByteAddress](loadTransactionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
address: address,
blockNumber: blockNumber
)
self.status.tasks.threadpool.start(arg)
const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[ResolveEnsTaskArg](argEncoded)
output = %* { "address": status_ens.address(arg.ens), "uuid": arg.uuid }
arg.finish(output)
proc resolveEns[T](self: T, slot: string, ens: string, uuid: string) =
let arg = ResolveEnsTaskArg(
tptr: cast[ByteAddress](resolveEnsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
ens: ens,
uuid: uuid
)
self.status.tasks.threadpool.start(arg)
QtObject:
type
@ -60,8 +214,8 @@ QtObject:
result.currentAssetList = newAssetList()
result.currentTransactions = newTransactionList()
result.currentCollectiblesLists = newCollectiblesList()
result.defaultTokenList = newTokenList()
result.customTokenList = newTokenList()
result.defaultTokenList = newTokenList(status)
result.customTokenList = newTokenList(status)
result.totalFiatBalance = ""
result.etherscanLink = ""
result.safeLowGasPrice = "0"
@ -292,17 +446,7 @@ QtObject:
self.transactionWasSent(txResult)
proc sendTransaction*(self: WalletView, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) {.slot.} =
let wallet = self.status.wallet
if assetAddress != ZERO_ADDRESS and not assetAddress.isEmptyOrWhitespace:
spawnAndSend(self, "transactionSent") do:
var success: bool
let response = wallet.sendTokenTransaction(from_addr, to, assetAddress, value, gas, gasPrice, password, success)
$(%* { "result": %response, "success": %success, "uuid": %uuid })
else:
spawnAndSend(self, "transactionSent") do:
var success: bool
let response = wallet.sendTransaction(from_addr, to, value, gas, gasPrice, password, success)
$(%* { "result": %response, "success": %success, "uuid": %uuid })
self.sendTransaction("transactionSent", from_addr, to, assetAddress, value, gas, gasPrice, password, uuid)
proc getDefaultAccount*(self: WalletView): string {.slot.} =
self.currentAccount.address
@ -364,15 +508,7 @@ QtObject:
for acc in self.status.wallet.accounts:
let accountAddress = acc.address
let tokenList = acc.assetList.filter(proc(x:Asset): bool = x.address != "").map(proc(x: Asset): string = x.address)
spawnAndSend(self, "getAccountBalanceSuccess") do:
var tokenBalances = initTable[string, string]()
for token in tokenList:
tokenBalances[token] = getTokenBalance(token, accountAddress)
$ %* {
"address": accountAddress,
"eth": getEthBalance(accountAddress),
"tokens": tokenBalances
}
self.initBalances("getAccountBalanceSuccess", accountAddress, tokenList)
proc getAccountBalanceSuccess*(self: WalletView, jsonResponse: string) {.slot.} =
let jsonObj = jsonResponse.parseJson()
@ -396,31 +532,11 @@ QtObject:
))
# TODO find a way to use a loop to streamline this code
# Spawn for each collectible. They can end in whichever order
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.CRYPTOKITTY,
"collectiblesOrError": status_collectibles.getCryptoKitties(address)
})
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.KUDO,
"collectiblesOrError": status_collectibles.getKudos(address)
})
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.ETHERMON,
"collectiblesOrError": status_collectibles.getEthermons(address)
})
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.STICKER,
"collectiblesOrError": status_collectibles.getStickers(address)
})
# Create a thread in the threadpool for each collectible. They can end in whichever order
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.CRYPTOKITTY)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.KUDO)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.ETHERMON)
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.STICKER)
proc setCollectiblesResult(self: WalletView, collectiblesJSON: string) {.slot.} =
let collectibleData = parseJson(collectiblesJSON)
@ -452,47 +568,13 @@ QtObject:
proc reloadCollectible*(self: WalletView, collectibleType: string) {.slot.} =
let address = self.currentAccount.address
# TODO find a cooler way to do this
case collectibleType:
of CRYPTOKITTY:
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.CRYPTOKITTY,
"collectiblesOrError": status_collectibles.getCryptoKitties(address)
})
of KUDO:
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.KUDO,
"collectiblesOrError": status_collectibles.getKudos(address)
})
of ETHERMON:
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.ETHERMON,
"collectiblesOrError": status_collectibles.getEthermons(address)
})
of status_collectibles.STICKER:
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.STICKER,
"collectiblesOrError": status_collectibles.getStickers(address)
})
else:
error "Unrecognized collectible"
return
self.loadCollectibles("setCollectiblesResult", address, collectibleType)
self.currentCollectiblesLists.setLoadingByType(collectibleType, 1)
proc gasPricePredictionsChanged*(self: WalletView) {.signal.}
proc getGasPricePredictions*(self: WalletView) {.slot.} =
spawnAndSend(self, "getGasPricePredictionsResult") do:
$ %getGasPricePredictions2()
self.getGasPredictions("getGasPricePredictionsResult")
proc getGasPricePredictionsResult(self: WalletView, gasPricePredictionsJson: string) {.slot.} =
let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction)
@ -586,11 +668,7 @@ QtObject:
# spawn'ed function cannot have a 'var' parameter
let blockNumber = bn
self.loadingTrxHistoryChanged(true)
spawnAndSend(self, "setTrxHistoryResult") do:
$(%*{
"address": address,
"history": getTransfersByAddress(address, blockNumber)
})
self.loadTransactions("setTrxHistoryResult", address, bn)
proc setTrxHistoryResult(self: WalletView, historyJSON: string) {.slot.} =
let historyData = parseJson(historyJSON)
@ -605,8 +683,7 @@ QtObject:
self.loadingTrxHistoryChanged(false)
proc resolveENS*(self: WalletView, ens: string, uuid: string) {.slot.} =
spawnAndSend(self, "ensResolved") do:
$ %* { "address": status_ens.address(ens), "uuid": uuid }
self.resolveEns("ensResolved", ens, uuid)
proc ensWasResolved*(self: WalletView, resolvedAddress: string, uuid: string) {.signal.}

View File

@ -1,7 +1,13 @@
import NimQml, tables, json
import ../../../status/libstatus/[tokens, settings, utils, eth/contracts]
import # nim libs
tables, json
import # vendor libs
NimQml
import # status-desktop libs
../../../status/libstatus/[tokens, settings, utils, eth/contracts],
../../../status/tasks/[qt, task_runner_impl], ../../../status/status
from web3/conversions import `$`
import ../../../status/threads
type
TokenRoles {.pure.} = enum
@ -11,9 +17,34 @@ type
Address = UserRole + 4,
Decimals = UserRole + 5
IsCustom = UserRole + 6
GetTokenDetailsTaskArg = ref object of QObjectTaskArg
address: string
const getTokenDetailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[GetTokenDetailsTaskArg](argEncoded)
tkn = newErc20Contract(getCurrentNetwork(), arg.address.parseAddress)
decimals = tkn.tokenDecimals()
let output = %* {
"address": arg.address,
"name": tkn.tokenName(),
"symbol": tkn.tokenSymbol(),
"decimals": (if decimals == 0: "" else: $decimals)
}
arg.finish(output)
proc getTokenDetails[T](self: T, slot: string, address: string) =
let arg = GetTokenDetailsTaskArg(
tptr: cast[ByteAddress](getTokenDetailsTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
address: address)
self.status.tasks.threadpool.start(arg)
QtObject:
type TokenList* = ref object of QAbstractListModel
status*: Status
tokens*: seq[Erc20Contract]
isCustom*: bool
@ -39,9 +70,10 @@ QtObject:
self.isCustom = true
self.endResetModel()
proc newTokenList*(): TokenList =
proc newTokenList*(status: Status): TokenList =
new(result, delete)
result.tokens = @[]
result.status = status
result.setup
proc rowData(self: TokenList, index: int, column: string): string {.slot.} =
@ -82,17 +114,7 @@ QtObject:
TokenRoles.IsCustom.int:"isCustom"}.toTable
proc getTokenDetails*(self: TokenList, address: string) {.slot.} =
spawnAndSend(self, "tokenDetailsResolved") do:
let tkn = newErc20Contract(getCurrentNetwork(), address.parseAddress)
let decimals = tkn.tokenDecimals()
$ (%* {
"address": address,
"name": tkn.tokenName(),
"symbol": tkn.tokenSymbol(),
"decimals": (if decimals == 0: "" else: $decimals)
})
self.getTokenDetails("tokenDetailsResolved", address)
proc tokenDetailsWereResolved*(self: TokenList, tokenDetails: string) {.signal.}

View File

@ -10,13 +10,11 @@ import app/onboarding/core as onboarding
import app/login/core as login
import app/provider/core as provider
import status/signals/core as signals
import status/tasks/task_manager
import status/libstatus/types
import status/libstatus/accounts/constants
import status_go
import status/status as statuslib
import ./eventemitter
import chronos, task_runner
var signalsQObjPointer: pointer
@ -30,9 +28,7 @@ proc mainProc() =
else:
"/../fleets.json"
let taskManager = newTaskManager()
taskManager.init()
let status = statuslib.newStatusInstance(taskManager, readFile(joinPath(getAppDir(), fleets)))
let status = statuslib.newStatusInstance(readFile(joinPath(getAppDir(), fleets)))
status.initNode()
enableHDPI()
@ -157,7 +153,7 @@ proc mainProc() =
profile.delete()
utilsController.delete()
browserController.delete()
taskManager.teardown()
status.tasks.teardown()
# Initialize only controllers whose init functions
@ -205,4 +201,3 @@ proc mainProc() =
when isMainModule:
mainProc()
GC_fullcollect()

View File

@ -4,9 +4,9 @@ import libstatus/settings as libstatus_settings
import libstatus/types as libstatus_types
import chat, accounts, wallet, node, network, mailservers, messages, contacts, profile, stickers, permissions, fleet
import ../eventemitter
import tasks/task_manager
import ./tasks/task_runner_impl
export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions, fleet
export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions, fleet, task_runner_impl
type Status* = ref object
events*: EventEmitter
@ -22,11 +22,11 @@ type Status* = ref object
network*: NetworkModel
stickers*: StickersModel
permissions*: PermissionsModel
taskManager*: TaskManager
tasks*: TaskRunner
proc newStatusInstance*(taskManager: TaskManager, fleetConfig: string): Status =
proc newStatusInstance*(fleetConfig: string): Status =
result = Status()
result.taskManager = taskManager
result.tasks = newTaskRunner()
result.events = createEventEmitter()
result.fleet = fleet.newFleetModel(result.events, fleetConfig)
result.chat = chat.newChatModel(result.events)
@ -42,7 +42,8 @@ proc newStatusInstance*(taskManager: TaskManager, fleetConfig: string): Status =
result.stickers = stickers.newStickersModel(result.events)
result.permissions = permissions.newPermissionsModel(result.events)
proc initNode*(self: Status) =
proc initNode*(self: Status) =
self.tasks.init()
libstatus_accounts.initNode()
proc startMessenger*(self: Status) =
@ -51,7 +52,7 @@ proc startMessenger*(self: Status) =
proc reset*(self: Status) =
# TODO: remove this once accounts are not tracked in the AccountsModel
self.accounts.reset()
# NOT NEEDED self.chat.reset()
# NOT NEEDED self.wallet.reset()
# NOT NEEDED self.node.reset()

View File

@ -1,17 +1,13 @@
import
import # vendor libs
json_serialization, NimQml, task_runner
type
BaseTasks* = ref object of RootObj
chanSendToPool*: AsyncChannel[ThreadSafeString]
BaseTask* = ref object of RootObj
vptr*: ByteAddress
slot*: string
Task* = proc(arg: string): void {.gcsafe, nimcall.}
TaskArg* = ref object of RootObj
tptr*: ByteAddress
proc start*[T: BaseTask](self: BaseTasks, task: T) =
let payload = task.toJson(typeAnnotations = true)
self.chanSendToPool.sendSync(payload.safe)
proc decode*[T](arg: string): T =
Json.decode(arg, T, allowUnknownFields = true)
proc finish*[T](task: BaseTask, payload: T) =
let resultPayload = Json.encode(payload)
signal_handler(cast[pointer](task.vptr), resultPayload, task.slot)
proc encode*[T](arg: T): string =
arg.toJson(typeAnnotations = true)

16
src/status/tasks/qt.nim Normal file
View File

@ -0,0 +1,16 @@
import # vendor libs
NimQml, json_serialization
import # status-desktop libs
./common
type
QObjectTaskArg* = ref object of TaskArg
vptr*: ByteAddress
slot*: string
proc finish*[T](arg: QObjectTaskArg, payload: T) =
signal_handler(cast[pointer](arg.vptr), Json.encode(payload), arg.slot)
proc finish*(arg: QObjectTaskArg, payload: string) =
signal_handler(cast[pointer](arg.vptr), payload, arg.slot)

View File

@ -1,50 +0,0 @@
import # nim libs
tables
import # vendor libs
chronos, NimQml, json, json_serialization, task_runner
import # status-desktop libs
./common, ../libstatus/types, ../stickers
type
StickerPackPurchaseGasEstimate* = ref object of BaseTask
packId*: int
address*: string
price*: string
uuid*: string
ObtainAvailableStickerPacks* = ref object of BaseTask
StickersTasks* = ref object of BaseTasks
proc newStickersTasks*(chanSendToPool: AsyncChannel[ThreadSafeString]): StickersTasks =
new(result)
result.chanSendToPool = chanSendToPool
proc run*(task: StickerPackPurchaseGasEstimate) =
var success: bool
var estimate = estimateGas(
task.packId,
task.address,
task.price,
success
)
if not success:
estimate = 325000
let result: tuple[estimate: int, uuid: string] = (estimate, task.uuid)
task.finish(result)
proc run*(task: ObtainAvailableStickerPacks) =
var success: bool
let availableStickerPacks = getAvailableStickerPacks()
var packs: seq[StickerPack] = @[]
for packId, stickerPack in availableStickerPacks.pairs:
packs.add(stickerPack)
task.finish(%*(packs))
proc stickerPackPurchaseGasEstimate*(self: StickersTasks, vptr: pointer, slot: string, packId: int, address: string, price: string, uuid: string) =
let task = StickerPackPurchaseGasEstimate(vptr: cast[ByteAddress](vptr), slot: slot, packId: packId, address: address, price: price, uuid: uuid)
self.start(task)
proc obtainAvailableStickerPacks*(self: StickersTasks, vptr: pointer, slot: string) =
let task = ObtainAvailableStickerPacks(vptr: cast[ByteAddress](vptr), slot: slot)
self.start(task)

View File

@ -1,27 +0,0 @@
import # vendor libs
chronicles, task_runner
import # status-desktop libs
./threadpool
export threadpool
logScope:
topics = "task-manager"
type
TaskManager* = ref object
threadPool*: ThreadPool
proc newTaskManager*(): TaskManager =
new(result)
result.threadPool = newThreadPool()
proc init*(self: TaskManager) =
self.threadPool.init()
proc teardown*(self: TaskManager) =
self.threadPool.teardown()

View File

@ -0,0 +1,24 @@
import # vendor libs
chronicles, task_runner
import # status-desktop libs
./threadpool
export task_runner, threadpool
logScope:
topics = "task-runner"
type
TaskRunner* = ref object
threadpool*: ThreadPool
proc newTaskRunner*(): TaskRunner =
new(result)
result.threadpool = newThreadPool()
proc init*(self: TaskRunner) =
self.threadpool.init()
proc teardown*(self: TaskRunner) =
self.threadpool.teardown()

View File

@ -1,26 +1,28 @@
import
chronicles, chronos, json, json_serialization, NimQml, sequtils, tables,
task_runner
import # std libs
json, sequtils, tables
import # vendor libs
chronicles, chronos, json_serialization, NimQml, task_runner
import # status-desktop libs
./common
import
./common, ./stickers
export
stickers
chronos, common, json_serialization
logScope:
topics = "task-threadpool"
type
ThreadPool* = ref object
chanRecvFromPool*: AsyncChannel[ThreadSafeString]
chanSendToPool*: AsyncChannel[ThreadSafeString]
chanRecvFromPool: AsyncChannel[ThreadSafeString]
chanSendToPool: AsyncChannel[ThreadSafeString]
thread: Thread[PoolThreadArg]
size: int
stickers*: StickersTasks
PoolThreadArg* = object
chanSendToMain*: AsyncChannel[ThreadSafeString]
chanRecvFromMain*: AsyncChannel[ThreadSafeString]
size*: int
PoolThreadArg = object
chanSendToMain: AsyncChannel[ThreadSafeString]
chanRecvFromMain: AsyncChannel[ThreadSafeString]
size: int
TaskThreadArg = object
id: int
chanRecvFromPool: AsyncChannel[ThreadSafeString]
@ -28,7 +30,6 @@ type
ThreadNotification = object
id: int
notice: string
# forward declarations
proc poolThread(arg: PoolThreadArg) {.thread.}
@ -41,7 +42,6 @@ proc newThreadPool*(size: int = MaxThreadPoolSize): ThreadPool =
result.chanSendToPool = newAsyncChannel[ThreadSafeString](-1)
result.thread = Thread[PoolThreadArg]()
result.size = size
result.stickers = newStickersTasks(result.chanSendToPool)
proc init*(self: ThreadPool) =
self.chanRecvFromPool.open()
@ -52,64 +52,60 @@ proc init*(self: ThreadPool) =
size: self.size
)
createThread(self.thread, poolThread, arg)
# block until we receive "ready"
let received = $(self.chanRecvFromPool.recvSync())
discard $(self.chanRecvFromPool.recvSync())
proc teardown*(self: ThreadPool) =
self.chanSendToPool.sendSync("shutdown".safe)
self.chanRecvFromPool.close()
self.chanSendToPool.close()
debug "[threadpool] waiting for the control thread to stop"
joinThread(self.thread)
proc task(arg: TaskThreadArg) {.async.} =
proc start*[T: TaskArg](self: Threadpool, arg: T) =
self.chanSendToPool.sendSync(arg.encode.safe)
proc runner(arg: TaskThreadArg) {.async.} =
arg.chanRecvFromPool.open()
arg.chanSendToPool.open()
let noticeToPool = ThreadNotification(id: arg.id, notice: "ready")
info "[threadpool task thread] sending 'ready'", threadid=arg.id
await arg.chanSendToPool.send(noticeToPool.toJson(typeAnnotations = true).safe)
debug "[threadpool task thread] sending 'ready'", threadid=arg.id
await arg.chanSendToPool.send(noticeToPool.encode.safe)
while true:
info "[threadpool task thread] waiting for message"
debug "[threadpool task thread] waiting for message"
let received = $(await arg.chanRecvFromPool.recv())
if received == "shutdown":
info "[threadpool task thread] received 'shutdown'"
info "[threadpool task thread] breaking while loop"
debug "[threadpool task thread] received 'shutdown'"
break
let
jsonNode = parseJson(received)
messageType = jsonNode{"$type"}.getStr
info "[threadpool task thread] received task", messageType=messageType
info "[threadpool task thread] initiating task", messageType=messageType,
parsed = parseJson(received)
messageType = parsed{"$type"}.getStr
debug "[threadpool task thread] initiating task", messageType=messageType,
threadid=arg.id
try:
case messageType
of "StickerPackPurchaseGasEstimate:ObjectType":
let decoded = Json.decode(received, StickerPackPurchaseGasEstimate, allowUnknownFields = true)
decoded.run()
of "ObtainAvailableStickerPacks:ObjectType":
let decoded = Json.decode(received, ObtainAvailableStickerPacks, allowUnknownFields = true)
decoded.run()
else:
error "[threadpool task thread] unknown message", message=received
let task = cast[Task](parsed{"tptr"}.getInt)
try:
task(received)
except Exception as e:
error "[threadpool task thread] exception", error=e.msg
except Exception as e:
error "[threadpool task thread] exception", error=e.msg
error "[threadpool task thread] unknown message", message=received
let noticeToPool = ThreadNotification(id: arg.id, notice: "done")
info "[threadpool task thread] sending 'done' notice to pool",
debug "[threadpool task thread] sending 'done' notice to pool",
threadid=arg.id
await arg.chanSendToPool.send(noticeToPool.toJson(typeAnnotations = true).safe)
await arg.chanSendToPool.send(noticeToPool.encode.safe)
arg.chanRecvFromPool.close()
arg.chanSendToPool.close()
proc taskThread(arg: TaskThreadArg) {.thread.} =
waitFor task(arg)
waitFor runner(arg)
proc pool(arg: PoolThreadArg) {.async.} =
let
@ -124,14 +120,14 @@ proc pool(arg: PoolThreadArg) {.async.} =
chanSendToMain.open()
chanRecvFromMainOrTask.open()
info "[threadpool] sending 'ready' to main thread"
debug "[threadpool] sending 'ready' to main thread"
await chanSendToMain.send("ready".safe)
for i in 0..<arg.size:
let id = i + 1
let chanSendToTask = newAsyncChannel[ThreadSafeString](-1)
chanSendToTask.open()
info "[threadpool] adding to threadsIdle", threadid=id
debug "[threadpool] adding to threadsIdle", threadid=id
threadsIdle[i].id = id
createThread(
threadsIdle[i].thr,
@ -153,55 +149,53 @@ proc pool(arg: PoolThreadArg) {.async.} =
# push thread into threadsIdle
while true:
info "[threadpool] waiting for message"
debug "[threadpool] waiting for message"
var task = $(await chanRecvFromMainOrTask.recv())
info "[threadpool] received message", msg=task
if task == "shutdown":
info "[threadpool] sending 'shutdown' to all task threads"
debug "[threadpool] sending 'shutdown' to all task threads"
for tpl in threadsIdle:
await tpl.chanSendToTask.send("shutdown".safe)
for tpl in threadsBusy.values:
await tpl.chanSendToTask.send("shutdown".safe)
info "[threadpool] breaking while loop"
break
let
jsonNode = parseJson(task)
messageType = jsonNode{"$type"}.getStr
info "[threadpool] determined message type", messageType=messageType
debug "[threadpool] determined message type", messageType=messageType
case messageType
of "ThreadNotification":
try:
let notification = Json.decode(task, ThreadNotification, allowUnknownFields = true)
info "[threadpool] received notification",
let notification = decode[ThreadNotification](task)
debug "[threadpool] received notification",
notice=notification.notice, threadid=notification.id
if notification.notice == "ready":
info "[threadpool] received 'ready' from a task thread"
debug "[threadpool] received 'ready' from a task thread"
allReady = allReady + 1
elif notification.notice == "done":
let tpl = threadsBusy[notification.id]
info "[threadpool] adding to threadsIdle",
debug "[threadpool] adding to threadsIdle",
newlength=(threadsIdle.len + 1)
threadsIdle.add (notification.id, tpl.thr, tpl.chanSendToTask)
info "[threadpool] removing from threadsBusy",
debug "[threadpool] removing from threadsBusy",
newlength=(threadsBusy.len - 1), threadid=notification.id
threadsBusy.del notification.id
if taskQueue.len > 0:
info "[threadpool] removing from taskQueue",
debug "[threadpool] removing from taskQueue",
newlength=(taskQueue.len - 1)
task = taskQueue[0]
taskQueue.delete 0, 0
info "[threadpool] removing from threadsIdle",
debug "[threadpool] removing from threadsIdle",
newlength=(threadsIdle.len - 1)
let tpl = threadsIdle[0]
threadsIdle.delete 0, 0
info "[threadpool] adding to threadsBusy",
debug "[threadpool] adding to threadsBusy",
newlength=(threadsBusy.len + 1), threadid=tpl.id
threadsBusy.add tpl.id, (tpl.thr, tpl.chanSendToTask)
await tpl.chanSendToTask.send(task.safe)
@ -214,7 +208,7 @@ proc pool(arg: PoolThreadArg) {.async.} =
else: # must be a request to do task work
if allReady < arg.size or threadsBusy.len == arg.size:
# add to queue
info "[threadpool] adding to taskQueue",
debug "[threadpool] adding to taskQueue",
newlength=(taskQueue.len + 1)
taskQueue.add task
@ -223,24 +217,23 @@ proc pool(arg: PoolThreadArg) {.async.} =
# check if we have tasks waiting on queue
if taskQueue.len > 0:
# remove first element from the task queue
info "[threadpool] adding to taskQueue",
debug "[threadpool] adding to taskQueue",
newlength=(taskQueue.len + 1)
taskQueue.add task
info "[threadpool] removing from taskQueue",
debug "[threadpool] removing from taskQueue",
newlength=(taskQueue.len - 1)
task = taskQueue[0]
taskQueue.delete 0, 0
info "[threadpool] removing from threadsIdle",
debug "[threadpool] removing from threadsIdle",
newlength=(threadsIdle.len - 1)
let tpl = threadsIdle[0]
threadsIdle.delete 0, 0
info "[threadpool] adding to threadsBusy",
debug "[threadpool] adding to threadsBusy",
newlength=(threadsBusy.len + 1), threadid=tpl.id
threadsBusy.add tpl.id, (tpl.thr, tpl.chanSendToTask)
await tpl.chanSendToTask.send(task.safe)
var allTaskThreads: seq[Thread[TaskThreadArg]] = @[]
for tpl in threadsIdle:
@ -253,6 +246,7 @@ proc pool(arg: PoolThreadArg) {.async.} =
chanSendToMain.close()
chanRecvFromMainOrTask.close()
debug "[threadpool] waiting for all task threads to stop"
joinThreads(allTaskThreads)
proc poolThread(arg: PoolThreadArg) {.thread.} =

View File

@ -1,12 +0,0 @@
import nimqml
import threadpool
import stew/faux_closures
export fauxClosure
template spawnAndSend*(view: untyped, signalName: string, exprBlock: untyped) =
let viewPtr = cast[pointer](view.vptr)
proc backgroundTask() {.fauxClosure.} =
let data = exprBlock
signal_handler(viewPtr, data, signalName)
spawn backgroundTask()

View File

@ -63,7 +63,7 @@ proc initEvents*(self: WalletModel) =
proc delete*(self: WalletModel) =
discard
proc buildTokenTransaction(self: WalletModel, source, to, assetAddress: Address, value: float, transfer: var Transfer, contract: var Erc20Contract, gas = "", gasPrice = ""): EthSend =
proc buildTokenTransaction(source, to, assetAddress: Address, value: float, transfer: var Transfer, contract: var Erc20Contract, gas = "", gasPrice = ""): EthSend =
contract = getErc20Contract(assetAddress)
if contract == nil:
raise newException(ValueError, fmt"Could not find ERC-20 contract with address '{assetAddress}' for the current network")
@ -114,7 +114,7 @@ proc estimateTokenGas*(self: WalletModel, source, to, assetAddress, value: strin
var
transfer: Transfer
contract: Erc20Contract
tx = self.buildTokenTransaction(
tx = buildTokenTransaction(
parseAddress(source),
parseAddress(to),
parseAddress(assetAddress),
@ -125,7 +125,7 @@ proc estimateTokenGas*(self: WalletModel, source, to, assetAddress, value: strin
result = contract.methods["transfer"].estimateGas(tx, transfer, success)
proc sendTransaction*(self: WalletModel, source, to, value, gas, gasPrice, password: string, success: var bool, data = ""): string =
proc sendTransaction*(source, to, value, gas, gasPrice, password: string, success: var bool, data = ""): string =
var tx = transactions.buildTransaction(
parseAddress(source),
eth2Wei(parseFloat(value), 18), gas, gasPrice, data
@ -138,11 +138,11 @@ proc sendTransaction*(self: WalletModel, source, to, value, gas, gasPrice, passw
if success:
trackPendingTransaction(result, $source, $to, PendingTransactionType.WalletTransfer, "")
proc sendTokenTransaction*(self: WalletModel, source, to, assetAddress, value, gas, gasPrice, password: string, success: var bool): string =
proc sendTokenTransaction*(source, to, assetAddress, value, gas, gasPrice, password: string, success: var bool): string =
var
transfer: Transfer
contract: Erc20Contract
tx = self.buildTokenTransaction(
tx = buildTokenTransaction(
parseAddress(source),
parseAddress(to),
parseAddress(assetAddress),
@ -333,22 +333,7 @@ proc getTransfersByAddress*(self: WalletModel, address: string): seq[Transaction
proc validateMnemonic*(self: WalletModel, mnemonic: string): string =
result = status_wallet.validateMnemonic(mnemonic).parseJSON()["error"].getStr
proc getGasPricePredictions*(self: WalletModel): GasPricePrediction =
if status_settings.getCurrentNetwork() != Network.Mainnet:
# TODO: what about other chains like xdai?
return GasPricePrediction(safeLow: 1.0, standard: 2.0, fast: 3.0, fastest: 4.0)
try:
let url: string = fmt"https://etherchain.org/api/gasPriceOracle"
let secureSSLContext = newContext()
let client = newHttpClient(sslContext = secureSSLContext)
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
let response = client.request(url)
result = Json.decode(response.body, GasPricePrediction)
except Exception as e:
echo "error getting gas price predictions"
echo e.msg
proc getGasPricePredictions2*(): GasPricePrediction =
proc getGasPricePredictions*(): GasPricePrediction =
if status_settings.getCurrentNetwork() != Network.Mainnet:
# TODO: what about other chains like xdai?
return GasPricePrediction(safeLow: 1.0, standard: 2.0, fast: 3.0, fastest: 4.0)