From 66912fd811848478a0f7afedf52e25c500c62bc5 Mon Sep 17 00:00:00 2001 From: Eric Mastro Date: Wed, 17 Mar 2021 17:25:41 +1100 Subject: [PATCH] feat: introduce Task Manager threadpool The `TaskManager` threadpool is a memory-safe replacement for the `spawnAndSend` operations that are currently causing memory issues in status-desktop. From a fundamental memory management point of view, `libstatus/settings`, `libstatus/contracts`, and `libstatus/tokens` (custom tokens) have all been converted to `{.threadvar.}`s and `Atomic[bool]`s to maintain the cache and `dirty` flag across threads, respectively, eliminating the need for thread locks and incorrect `{.gcsafe.}` compiler overrides. The successful [recyclable threadpool experiment from `nim-task-runner`](https://github.com/status-im/nim-task-runner/blob/test/use-cases/test/use_cases/test_sync.nim) using `AsyncChannel[ThreadSafeString]`s was brought over to `status-desktop` and implemented in somewhat of a hardcoded manner, as we knew this would save some time instead of trying to create a fully fleshed out `nim-task-runner` API and build a miraculous macro that may or may not be able to generate the needed API. The threadpool is started by the `TaskManager` and both the `TaskManager` and the `TaskManager`'s threadpool are started as early as possible in the application lifecycle (in `nim_status_client.nim`). The `TaskManager` creates a thread to run the threadpool. During its initialization, the threadpool then spools up all the threads it will manage and puts them in an idle thread sequence. This is to prevent expensive thread creation and teardown happening during the app's lifetime as it is quite expensive and blocks the main thread. When tasks comes in to the pool, the task is sent to an idle thread, or put in a queue if all threads are busy. The idle thread is moved to the busy thread sequence. When a task is completed, the thread is taken out of the busy threads sequence and moved back in to the sequence of idle threads, effectively recycling it. The first `spawnAndSend` we were able to change over to the new threadpool was `estimate`, which estimates the gas of a sticker purchase transaction. From the consumer point of view, the existing `spawnAndSend` to achieve this looks like: ```nim proc estimate*(self: StickersView, packId: int, address: string, price: string, uuid: string) {.slot.} = let status_stickers = self.status.stickers spawnAndSend(self, "setGasEstimate") do: var success: bool var estimate = status_stickers.estimateGas(packId, address, price, success) if not success: estimate = 325000 let result: tuple[estimate: int, uuid: string] = (estimate, uuid) Json.encode(result) ``` And the new syntax looks like this: ```nim 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) ``` The logic inside the `spawnAndSend` body was moved to [src/status/tasks/stickers.nim](https://github.com/status-im/status-desktop/compare/experiment/tasks-3?expand=1#diff-09e57eef00b0cee5c4abdb9039f948d8372e7003e09e934a9b4c7e9167d47658). This is just the first migration of `spawnAndSend`, however moving the majority of the remaining `spawnAndSend`s will likely just be an exercise in copy/pasta. There will be one or two that may require a bit more thinking, depending how they rely on data from the model. Once the `spawnAndSend`s have been converted to the threadpool, we can start implementing the [long-running process from the task runner use case experiments](https://github.com/status-im/nim-task-runner/blob/test/use-cases/test/use_cases/test_long_running.nim). And finally, we can then implement the [async tasks](https://github.com/status-im/nim-task-runner/blob/test/use-cases/test/use_cases/test_async.nim) if needed. @michaelsbradleyjr and I spent many hours digging in to the depths of nim's memory management in an attempt to understand it. We have created [a presentation with our task runner experiment findings](https://docs.google.com/presentation/d/1ItCxAfsVTcIoH_E4bgvmHljhbU-tC3T6K2A6ahwAedk/edit?usp=sharing), and @michaelsbradleyjr has spent time [answering questions off the back of that presentation.](https://gist.github.com/michaelsbradleyjr/1eaa9937b3fbb4ffff3fb814f0dd82a9). We have created a fork of `edn.nim` at `status-im/edn.nim` and we need the PR to be merged and the commit hash updated before we can merge this PR in to `status-desktop`. --- .gitignore | 1 + .gitmodules | 6 + src/app/chat/views/stickers.nim | 24 +- src/app/wallet/view.nim | 3 +- src/nim_status_client.nim | 10 +- src/status/ens.nim | 34 +-- src/status/libstatus/eth/contracts.nim | 382 ++++++++++++------------- src/status/libstatus/settings.nim | 59 ++-- src/status/libstatus/stickers.nim | 74 ++--- src/status/libstatus/tokens.nim | 60 ++-- src/status/status.nim | 5 +- src/status/stickers.nim | 8 +- src/status/tasks/stickers.nim | 40 +++ src/status/tasks/task_manager.nim | 27 ++ src/status/tasks/threadpool.nim | 256 +++++++++++++++++ src/status/wallet.nim | 15 + vendor/DOtherSide | 2 +- vendor/edn.nim | 2 +- vendor/nim-libp2p | 2 +- vendor/nim-task-runner | 1 + 20 files changed, 681 insertions(+), 330 deletions(-) create mode 100644 src/status/tasks/stickers.nim create mode 100644 src/status/tasks/task_manager.nim create mode 100644 src/status/tasks/threadpool.nim create mode 160000 vendor/nim-task-runner diff --git a/.gitignore b/.gitignore index 42ebec6aa6..65381e1023 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ noBackup/ *.pro.user *.pro.autosave *.qml.autosave +.update.timestamp .vscode bin/ /bottles/ diff --git a/.gitmodules b/.gitmodules index e741ea9740..3e1d9b65f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -91,3 +91,9 @@ [submodule "vendor/nim-status-go"] path = vendor/nim-status-go url = https://github.com/status-im/nim-status-go.git +[submodule "vendor/nim-task-runner"] + path = vendor/nim-task-runner + url = https://github.com/status-im/nim-task-runner.git +[submodule "vendor/edn.nim"] + path = vendor/edn.nim + url = https://github.com/status-im/edn.nim.git diff --git a/src/app/chat/views/stickers.nim b/src/app/chat/views/stickers.nim index 00e6e39887..25016b64aa 100644 --- a/src/app/chat/views/stickers.nim +++ b/src/app/chat/views/stickers.nim @@ -5,6 +5,7 @@ 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 logScope: topics = "stickers-view" @@ -30,10 +31,10 @@ QtObject: result.recentStickers = newStickerList() result.activeChannel = activeChannel result.setup - + proc addStickerPackToList*(self: StickersView, stickerPack: StickerPack, isInstalled, isBought, isPending: bool) = self.stickerPacks.addStickerPackToList(stickerPack, newStickerList(stickerPack.stickers), isInstalled, isBought, isPending) - + proc getStickerPackList(self: StickersView): QVariant {.slot.} = newQVariant(self.stickerPacks) @@ -41,19 +42,12 @@ QtObject: read = getStickerPackList proc transactionWasSent*(self: StickersView, txResult: string) {.signal.} - + proc transactionCompleted*(self: StickersView, success: bool, txHash: string, revertReason: string = "") {.signal.} proc estimate*(self: StickersView, packId: int, address: string, price: string, uuid: string) {.slot.} = - let status_stickers = self.status.stickers - spawnAndSend(self, "setGasEstimate") do: - var success: bool - var estimate = status_stickers.estimateGas(packId, address, price, success) - if not success: - estimate = 325000 - let result: tuple[estimate: int, uuid: string] = (estimate, uuid) - Json.encode(result) - + self.status.taskManager.threadPool.stickers.stickerPackPurchaseGasEstimate(cast[pointer](self.vptr), "setGasEstimate", packId, address, price, uuid) + proc gasEstimateReturned*(self: StickersView, estimate: int, uuid: string) {.signal.} proc setGasEstimate*(self: StickersView, estimateJson: string) {.slot.} = @@ -63,7 +57,7 @@ QtObject: proc buy*(self: StickersView, packId: int, address: string, price: string, gas: string, gasPrice: string, password: string): string {.slot.} = var success: bool let response = self.status.stickers.buyPack(packId, address, price, gas, gasPrice, password, success) - # TODO: + # TODO: # check if response["error"] is not null and handle the error result = $(%* { "result": %response, "success": %success }) if success: @@ -133,7 +127,7 @@ QtObject: proc resetBuyAttempt*(self: StickersView, packId: int) {.slot.} = self.stickerPacks.updateStickerPackInList(packId, false, false) - + proc uninstall*(self: StickersView, packId: int) {.slot.} = self.status.stickers.uninstallStickerPack(packId) self.status.stickers.removeRecentStickers(packId) @@ -156,4 +150,4 @@ QtObject: proc send*(self: StickersView, hash: string, pack: int) {.slot.} = let sticker = Sticker(hash: hash, packId: pack) self.addRecentStickerToList(sticker) - self.status.chat.sendSticker(self.activeChannel.id, sticker) \ No newline at end of file + self.status.chat.sendSticker(self.activeChannel.id, sticker) diff --git a/src/app/wallet/view.nim b/src/app/wallet/view.nim index 804c5a6416..9d4ffc80df 100644 --- a/src/app/wallet/view.nim +++ b/src/app/wallet/view.nim @@ -491,9 +491,8 @@ QtObject: proc gasPricePredictionsChanged*(self: WalletView) {.signal.} proc getGasPricePredictions*(self: WalletView) {.slot.} = - let walletModel = self.status.wallet spawnAndSend(self, "getGasPricePredictionsResult") do: - $ %walletModel.getGasPricePredictions() + $ %getGasPricePredictions2() proc getGasPricePredictionsResult(self: WalletView, gasPricePredictionsJson: string) {.slot.} = let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction) diff --git a/src/nim_status_client.nim b/src/nim_status_client.nim index 94fe69f325..d089d97a0d 100644 --- a/src/nim_status_client.nim +++ b/src/nim_status_client.nim @@ -10,11 +10,13 @@ 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 @@ -28,7 +30,9 @@ proc mainProc() = else: "/../fleets.json" - let status = statuslib.newStatusInstance(readFile(joinPath(getAppDir(), fleets))) + let taskManager = newTaskManager() + taskManager.init() + let status = statuslib.newStatusInstance(taskManager, readFile(joinPath(getAppDir(), fleets))) status.initNode() enableHDPI() @@ -132,6 +136,7 @@ proc mainProc() = wallet.checkPendingTransactions() wallet.start() + engine.setRootContextProperty("loginModel", login.variant) engine.setRootContextProperty("onboardingModel", onboarding.variant) @@ -152,6 +157,7 @@ proc mainProc() = profile.delete() utilsController.delete() browserController.delete() + taskManager.teardown() # Initialize only controllers whose init functions @@ -188,9 +194,7 @@ proc mainProc() = # it will be passed as a regular C function to libstatus. This means that # we cannot capture any local variables here (we must rely on globals) var callback: SignalCallback = proc(p0: cstring) {.cdecl.} = - setupForeignThreadGc() signal_handler(signalsQObjPointer, p0, "receiveSignal") - tearDownForeignThreadGc() status_go.setSignalEventCallback(callback) diff --git a/src/status/ens.nim b/src/status/ens.nim index 9ce98762ce..dd7d0227e6 100644 --- a/src/status/ens.nim +++ b/src/status/ens.nim @@ -21,12 +21,12 @@ const domain* = ".stateofus.eth" proc userName*(ensName: string, removeSuffix: bool = false): string = if ensName != "" and ensName.endsWith(domain): - if removeSuffix: + if removeSuffix: result = ensName.split(".")[0] else: result = ensName else: - if ensName.endsWith(".eth") and removeSuffix: + if ensName.endsWith(".eth") and removeSuffix: return ensName.split(".")[0] result = ensName @@ -63,7 +63,7 @@ proc namehash*(ensName:string): string = concatArrays[0..31] = node concatArrays[32..63] = elem node = keccak_256.digest(concatArrays).data - + result = "0x" & node.toHex() const registry* = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e" @@ -81,7 +81,7 @@ proc resolver*(usernameHash: string): string = result = "0x" & resolverAddr const owner_signature = "0x02571be3" # owner(bytes32 node) -proc owner*(username: string): string = +proc owner*(username: string): string = var userNameHash = namehash(addDomain(username)) userNameHash.removePrefix("0x") let payload = %* [{ @@ -97,7 +97,7 @@ proc owner*(username: string): string = result = "0x" & ownerAddr.substr(26) const pubkey_signature = "0xc8690233" # pubkey(bytes32 node) -proc pubkey*(username: string): string = +proc pubkey*(username: string): string = var userNameHash = namehash(addDomain(username)) userNameHash.removePrefix("0x") let ensResolver = resolver(userNameHash) @@ -116,7 +116,7 @@ proc pubkey*(username: string): string = result = "0x04" & pubkey const address_signature = "0x3b3b57de" # addr(bytes32 node) -proc address*(username: string): string = +proc address*(username: string): string = var userNameHash = namehash(addDomain(username)) userNameHash.removePrefix("0x") let ensResolver = resolver(userNameHash) @@ -133,7 +133,7 @@ proc address*(username: string): string = result = "0x" & address.substr(26) const contenthash_signature = "0xbc1c58d1" # contenthash(bytes32) -proc contenthash*(ensAddr: string): string = +proc contenthash*(ensAddr: string): string = var ensHash = namehash(ensAddr) ensHash.removePrefix("0x") let ensResolver = resolver(ensHash) @@ -147,7 +147,7 @@ proc contenthash*(ensAddr: string): string = let bytesResponse = response.parseJson["result"].getStr; if bytesResponse == "0x": return "" - + let size = fromHex(Stuint[256], bytesResponse[66..129]).truncate(int) result = bytesResponse[130..129+size*2] @@ -159,7 +159,7 @@ proc getPrice*(): Stuint[256] = "to": $contract.address, "data": contract.methods["getPrice"].encodeAbi() }, "latest"] - + let responseStr = callPrivateRPC("eth_call", payload) let response = Json.decode(responseStr, RpcResponse) if not response.error.isNil: @@ -180,7 +180,7 @@ proc registerUsernameEstimateGas*(username: string, address: string, pubKey: str ensUsernamesContract = contracts.getContract("ens-usernames") sntContract = contracts.getSntContract() price = getPrice() - + let register = Register(label: label, account: parseAddress(address), x: x, y: y) registerAbiEncoded = ensUsernamesContract.methods["register"].encodeAbi(register) @@ -188,7 +188,7 @@ proc registerUsernameEstimateGas*(username: string, address: string, pubKey: str approveAndCallAbiEncoded = sntContract.methods["approveAndCall"].encodeAbi(approveAndCallObj) var tx = transactions.buildTokenTransaction(parseAddress(address), sntContract.address, "", "") - + let response = sntContract.methods["approveAndCall"].estimateGas(tx, approveAndCallObj, success) if success: result = fromHex[int](response) @@ -202,11 +202,11 @@ proc registerUsername*(username, pubKey, address, gas, gasPrice, password: stri ensUsernamesContract = contracts.getContract("ens-usernames") sntContract = contracts.getSntContract() price = getPrice() - + let register = Register(label: label, account: parseAddress(address), x: x, y: y) registerAbiEncoded = ensUsernamesContract.methods["register"].encodeAbi(register) - approveAndCallObj = ApproveAndCall[132](to: ensUsernamesContract.address, value: price, data: DynamicBytes[132].fromHex(registerAbiEncoded)) + approveAndCallObj = ApproveAndCall[132](to: ensUsernamesContract.address, value: price, data: DynamicBytes[132].fromHex(registerAbiEncoded)) var tx = transactions.buildTokenTransaction(parseAddress(address), sntContract.address, gas, gasPrice) @@ -227,7 +227,7 @@ proc setPubKeyEstimateGas*(username: string, address: string, pubKey: string, su resolverAddress = resolver(hash) var tx = transactions.buildTokenTransaction(parseAddress(address), parseAddress(resolverAddress), "", "") - + try: let response = resolverContract.methods["setPubkey"].estimateGas(tx, setPubkey, success) if success: @@ -295,8 +295,10 @@ proc decodeENSContentHash*(value: string): tuple[ensType: ENSType, output: strin # 12 = identifies sha2-256 hash # 20 = multihash length = 32 # ...rest = multihash digest - let multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get() - return (ENSType.IPFS, $Cid.init(CIDv0, MultiCodec.codec(codec), multiHash)) + let + multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get() + decoded = Cid.init(CIDv0, MultiCodec.codec(codec), multiHash).get() + return (ENSType.IPFS, $decoded) except Exception as e: error "Error decoding ENS contenthash", hash=value, exception=e.msg raise diff --git a/src/status/libstatus/eth/contracts.nim b/src/status/libstatus/eth/contracts.nim index 5c98cf4e23..aa7896061b 100644 --- a/src/status/libstatus/eth/contracts.nim +++ b/src/status/libstatus/eth/contracts.nim @@ -1,5 +1,5 @@ import - sequtils, sugar, macros, tables, strutils, locks + sequtils, sugar, macros, tables, strutils import web3/ethtypes, stew/byteutils, nimcrypto, json_serialization, chronicles @@ -16,9 +16,6 @@ export logScope: topics = "contracts" -var contractsLock: Lock -initLock(contractsLock) - const ERC20_METHODS = @[ ("name", Method(signature: "name()")), ("symbol", Method(signature: "symbol()")), @@ -80,186 +77,193 @@ proc newErc721Contract(name: string, network: Network, address: Address, symbol: Erc721Contract(name: name, network: network, address: address, symbol: symbol, hasIcon: hasIcon, methods: ERC721_ENUMERABLE_METHODS.concat(addlMethods).toTable) -var ALL_CONTRACTS {.guard: contractsLock.}: seq[Contract] = @[ - # Mainnet contracts - newErc20Contract("Status Network Token", Network.Mainnet, parseAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"), "SNT", 18, true), - newErc20Contract("Dai Stablecoin", Network.Mainnet, parseAddress("0x6b175474e89094c44da98b954eedeac495271d0f"), "DAI", 18, true), - newErc20Contract("Sai Stablecoin v1.0", Network.Mainnet, parseAddress("0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"), "SAI", 18, true), - newErc20Contract("MKR", Network.Mainnet, parseAddress("0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2"), "MKR", 18, true), - newErc20Contract("EOS", Network.Mainnet, parseAddress("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"), "EOS", 18, true), - newErc20Contract("OMGToken", Network.Mainnet, parseAddress("0xd26114cd6ee289accf82350c8d8487fedb8a0c07"), "OMG", 18, true), - newErc20Contract("Populous Platform", Network.Mainnet, parseAddress("0xd4fa1460f537bb9085d22c7bccb5dd450ef28e3a"), "PPT", 8, true), - newErc20Contract("Reputation", Network.Mainnet, parseAddress("0x1985365e9f78359a9b6ad760e32412f4a445e862"), "REP", 18, true), - newErc20Contract("PowerLedger", Network.Mainnet, parseAddress("0x595832f8fc6bf59c85c527fec3740a1b7a361269"), "POWR", 6, true), - newErc20Contract("TenX Pay Token", Network.Mainnet, parseAddress("0xb97048628db6b661d4c2aa833e95dbe1a905b280"), "PAY", 18, true), - newErc20Contract("Veros", Network.Mainnet, parseAddress("0x92e78dae1315067a8819efd6dca432de9dcde2e9"), "VRS", 6, false), - newErc20Contract("Golem Network Token", Network.Mainnet, parseAddress("0xa74476443119a942de498590fe1f2454d7d4ac0d"), "GNT", 18, true), - newErc20Contract("Salt", Network.Mainnet, parseAddress("0x4156d3342d5c385a87d264f90653733592000581"), "SALT", 8, true), - newErc20Contract("BNB", Network.Mainnet, parseAddress("0xb8c77482e45f1f44de1745f52c74426c631bdd52"), "BNB", 18, true), - newErc20Contract("Basic Attention Token", Network.Mainnet, parseAddress("0x0d8775f648430679a709e98d2b0cb6250d2887ef"), "BAT", 18, true), - newErc20Contract("Kyber Network Crystal", Network.Mainnet, parseAddress("0xdd974d5c2e2928dea5f71b9825b8b646686bd200"), "KNC", 18, true), - newErc20Contract("BTU Protocol", Network.Mainnet, parseAddress("0xb683D83a532e2Cb7DFa5275eED3698436371cc9f"), "BTU", 18, true), - newErc20Contract("Digix DAO", Network.Mainnet, parseAddress("0xe0b7927c4af23765cb51314a0e0521a9645f0e2a"), "DGD", 9, true), - newErc20Contract("Aeternity", Network.Mainnet, parseAddress("0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d"), "AE", 18, true), - newErc20Contract("Tronix", Network.Mainnet, parseAddress("0xf230b790e05390fc8295f4d3f60332c93bed42e2"), "TRX", 6, true), - newErc20Contract("Ethos", Network.Mainnet, parseAddress("0x5af2be193a6abca9c8817001f45744777db30756"), "ETHOS", 8, true), - newErc20Contract("Raiden Token", Network.Mainnet, parseAddress("0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6"), "RDN", 18, true), - newErc20Contract("SingularDTV", Network.Mainnet, parseAddress("0xaec2e87e0a235266d9c5adc9deb4b2e29b54d009"), "SNGLS", 0, true), - newErc20Contract("Gnosis Token", Network.Mainnet, parseAddress("0x6810e776880c02933d47db1b9fc05908e5386b96"), "GNO", 18, true), - newErc20Contract("StorjToken", Network.Mainnet, parseAddress("0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac"), "STORJ", 8, true), - newErc20Contract("AdEx", Network.Mainnet, parseAddress("0x4470bb87d77b963a013db939be332f927f2b992e"), "ADX", 4, false), - newErc20Contract("FunFair", Network.Mainnet, parseAddress("0x419d0d8bdd9af5e606ae2232ed285aff190e711b"), "FUN", 8, true), - newErc20Contract("Civic", Network.Mainnet, parseAddress("0x41e5560054824ea6b0732e656e3ad64e20e94e45"), "CVC", 8, true), - newErc20Contract("ICONOMI", Network.Mainnet, parseAddress("0x888666ca69e0f178ded6d75b5726cee99a87d698"), "ICN", 18, true), - newErc20Contract("Walton Token", Network.Mainnet, parseAddress("0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74"), "WTC", 18, true), - newErc20Contract("Bytom", Network.Mainnet, parseAddress("0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750"), "BTM", 8, true), - newErc20Contract("0x Protocol Token", Network.Mainnet, parseAddress("0xe41d2489571d322189246dafa5ebde1f4699f498"), "ZRX", 18, true), - newErc20Contract("Bancor Network Token", Network.Mainnet, parseAddress("0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c"), "BNT", 18, true), - newErc20Contract("Metal", Network.Mainnet, parseAddress("0xf433089366899d83a9f26a773d59ec7ecf30355e"), "MTL", 8, false), - newErc20Contract("PayPie", Network.Mainnet, parseAddress("0xc42209accc14029c1012fb5680d95fbd6036e2a0"), "PPP", 18, true), - newErc20Contract("ChainLink Token", Network.Mainnet, parseAddress("0x514910771af9ca656af840dff83e8264ecf986ca"), "LINK", 18, true), - newErc20Contract("Kin", Network.Mainnet, parseAddress("0x818fc6c2ec5986bc6e2cbf00939d90556ab12ce5"), "KIN", 18, true), - newErc20Contract("Aragon Network Token", Network.Mainnet, parseAddress("0x960b236a07cf122663c4303350609a66a7b288c0"), "ANT", 18, true), - newErc20Contract("MobileGo Token", Network.Mainnet, parseAddress("0x40395044ac3c0c57051906da938b54bd6557f212"), "MGO", 8, true), - newErc20Contract("Monaco", Network.Mainnet, parseAddress("0xb63b606ac810a52cca15e44bb630fd42d8d1d83d"), "MCO", 8, true), - newErc20Contract("loopring", Network.Mainnet, parseAddress("0xef68e7c694f40c8202821edf525de3782458639f"), "LRC", 18, true), - newErc20Contract("Zeus Shield Coin", Network.Mainnet, parseAddress("0x7a41e0517a5eca4fdbc7fbeba4d4c47b9ff6dc63"), "ZSC", 18, true), - newErc20Contract("Streamr DATAcoin", Network.Mainnet, parseAddress("0x0cf0ee63788a0849fe5297f3407f701e122cc023"), "DATA", 18, true), - newErc20Contract("Ripio Credit Network Token", Network.Mainnet, parseAddress("0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6"), "RCN", 18, true), - newErc20Contract("WINGS", Network.Mainnet, parseAddress("0x667088b212ce3d06a1b553a7221e1fd19000d9af"), "WINGS", 18, true), - newErc20Contract("Edgeless", Network.Mainnet, parseAddress("0x08711d3b02c8758f2fb3ab4e80228418a7f8e39c"), "EDG", 0, true), - newErc20Contract("Melon Token", Network.Mainnet, parseAddress("0xbeb9ef514a379b997e0798fdcc901ee474b6d9a1"), "MLN", 18, true), - newErc20Contract("Moeda Loyalty Points", Network.Mainnet, parseAddress("0x51db5ad35c671a87207d88fc11d593ac0c8415bd"), "MDA", 18, true), - newErc20Contract("PILLAR", Network.Mainnet, parseAddress("0xe3818504c1b32bf1557b16c238b2e01fd3149c17"), "PLR", 18, true), - newErc20Contract("QRL", Network.Mainnet, parseAddress("0x697beac28b09e122c4332d163985e8a73121b97f"), "QRL", 8, true), - newErc20Contract("Modum Token", Network.Mainnet, parseAddress("0x957c30ab0426e0c93cd8241e2c60392d08c6ac8e"), "MOD", 0, true), - newErc20Contract("Token-as-a-Service", Network.Mainnet, parseAddress("0xe7775a6e9bcf904eb39da2b68c5efb4f9360e08c"), "TAAS", 6, true), - newErc20Contract("GRID Token", Network.Mainnet, parseAddress("0x12b19d3e2ccc14da04fae33e63652ce469b3f2fd"), "GRID", 12, true), - newErc20Contract("SANtiment network token", Network.Mainnet, parseAddress("0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098"), "SAN", 18, true), - newErc20Contract("SONM Token", Network.Mainnet, parseAddress("0x983f6d60db79ea8ca4eb9968c6aff8cfa04b3c63"), "SNM", 18, true), - newErc20Contract("Request Token", Network.Mainnet, parseAddress("0x8f8221afbb33998d8584a2b05749ba73c37a938a"), "REQ", 18, true), - newErc20Contract("Substratum", Network.Mainnet, parseAddress("0x12480e24eb5bec1a9d4369cab6a80cad3c0a377a"), "SUB", 2, true), - newErc20Contract("Decentraland MANA", Network.Mainnet, parseAddress("0x0f5d2fb29fb7d3cfee444a200298f468908cc942"), "MANA", 18, true), - newErc20Contract("AirSwap Token", Network.Mainnet, parseAddress("0x27054b13b1b798b345b591a4d22e6562d47ea75a"), "AST", 4, true), - newErc20Contract("R token", Network.Mainnet, parseAddress("0x48f775efbe4f5ece6e0df2f7b5932df56823b990"), "R", 0, true), - newErc20Contract("FirstBlood Token", Network.Mainnet, parseAddress("0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7"), "1ST", 18, true), - newErc20Contract("Cofoundit", Network.Mainnet, parseAddress("0x12fef5e57bf45873cd9b62e9dbd7bfb99e32d73e"), "CFI", 18, true), - newErc20Contract("Enigma", Network.Mainnet, parseAddress("0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4"), "ENG", 8, true), - newErc20Contract("Amber Token", Network.Mainnet, parseAddress("0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce"), "AMB", 18, true), - newErc20Contract("XPlay Token", Network.Mainnet, parseAddress("0x90528aeb3a2b736b780fd1b6c478bb7e1d643170"), "XPA", 18, true), - newErc20Contract("Open Trading Network", Network.Mainnet, parseAddress("0x881ef48211982d01e2cb7092c915e647cd40d85c"), "OTN", 18, true), - newErc20Contract("Trustcoin", Network.Mainnet, parseAddress("0xcb94be6f13a1182e4a4b6140cb7bf2025d28e41b"), "TRST", 6, true), - newErc20Contract("Monolith TKN", Network.Mainnet, parseAddress("0xaaaf91d9b90df800df4f55c205fd6989c977e73a"), "TKN", 8, true), - newErc20Contract("RHOC", Network.Mainnet, parseAddress("0x168296bb09e24a88805cb9c33356536b980d3fc5"), "RHOC", 8, true), - newErc20Contract("Target Coin", Network.Mainnet, parseAddress("0xac3da587eac229c9896d919abc235ca4fd7f72c1"), "TGT", 1, false), - newErc20Contract("Everex", Network.Mainnet, parseAddress("0xf3db5fa2c66b7af3eb0c0b782510816cbe4813b8"), "EVX", 4, true), - newErc20Contract("ICOS", Network.Mainnet, parseAddress("0x014b50466590340d41307cc54dcee990c8d58aa8"), "ICOS", 6, true), - newErc20Contract("district0x Network Token", Network.Mainnet, parseAddress("0x0abdace70d3790235af448c88547603b945604ea"), "DNT", 18, true), - newErc20Contract("Dentacoin", Network.Mainnet, parseAddress("0x08d32b0da63e2c3bcf8019c9c5d849d7a9d791e6"), "٨", 0, false), - newErc20Contract("Eidoo Token", Network.Mainnet, parseAddress("0xced4e93198734ddaff8492d525bd258d49eb388e"), "EDO", 18, true), - newErc20Contract("BitDice", Network.Mainnet, parseAddress("0x29d75277ac7f0335b2165d0895e8725cbf658d73"), "CSNO", 8, false), - newErc20Contract("Cobinhood Token", Network.Mainnet, parseAddress("0xb2f7eb1f2c37645be61d73953035360e768d81e6"), "COB", 18, true), - newErc20Contract("Enjin Coin", Network.Mainnet, parseAddress("0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c"), "ENJ", 18, false), - newErc20Contract("AVENTUS", Network.Mainnet, parseAddress("0x0d88ed6e74bbfd96b831231638b66c05571e824f"), "AVT", 18, false), - newErc20Contract("Chronobank TIME", Network.Mainnet, parseAddress("0x6531f133e6deebe7f2dce5a0441aa7ef330b4e53"), "TIME", 8, false), - newErc20Contract("Cindicator Token", Network.Mainnet, parseAddress("0xd4c435f5b09f855c3317c8524cb1f586e42795fa"), "CND", 18, true), - newErc20Contract("Stox", Network.Mainnet, parseAddress("0x006bea43baa3f7a6f765f14f10a1a1b08334ef45"), "STX", 18, true), - newErc20Contract("Xaurum", Network.Mainnet, parseAddress("0x4df812f6064def1e5e029f1ca858777cc98d2d81"), "XAUR", 8, true), - newErc20Contract("Vibe", Network.Mainnet, parseAddress("0x2c974b2d0ba1716e644c1fc59982a89ddd2ff724"), "VIB", 18, true), - newErc20Contract("PRG", Network.Mainnet, parseAddress("0x7728dfef5abd468669eb7f9b48a7f70a501ed29d"), "PRG", 6, false), - newErc20Contract("Delphy Token", Network.Mainnet, parseAddress("0x6c2adc2073994fb2ccc5032cc2906fa221e9b391"), "DPY", 18, true), - newErc20Contract("CoinDash Token", Network.Mainnet, parseAddress("0x2fe6ab85ebbf7776fee46d191ee4cea322cecf51"), "CDT", 18, true), - newErc20Contract("Tierion Network Token", Network.Mainnet, parseAddress("0x08f5a9235b08173b7569f83645d2c7fb55e8ccd8"), "TNT", 8, true), - newErc20Contract("DomRaiderToken", Network.Mainnet, parseAddress("0x9af4f26941677c706cfecf6d3379ff01bb85d5ab"), "DRT", 8, true), - newErc20Contract("SPANK", Network.Mainnet, parseAddress("0x42d6622dece394b54999fbd73d108123806f6a18"), "SPANK", 18, true), - newErc20Contract("Berlin Coin", Network.Mainnet, parseAddress("0x80046305aaab08f6033b56a360c184391165dc2d"), "BRLN", 18, true), - newErc20Contract("USD//C", Network.Mainnet, parseAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), "USDC", 6, true), - newErc20Contract("Livepeer Token", Network.Mainnet, parseAddress("0x58b6a8a3302369daec383334672404ee733ab239"), "LPT", 18, true), - newErc20Contract("Simple Token", Network.Mainnet, parseAddress("0x2c4e8f2d746113d0696ce89b35f0d8bf88e0aeca"), "ST", 18, true), - newErc20Contract("Wrapped BTC", Network.Mainnet, parseAddress("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"), "WBTC", 8, true), - newErc20Contract("Bloom Token", Network.Mainnet, parseAddress("0x107c4504cd79c5d2696ea0030a8dd4e92601b82e"), "BLT", 18, true), - Contract(name: "stickers", network: Network.Mainnet, address: parseAddress("0x0577215622f43a39f4bc9640806dfea9b10d2a36"), - methods: [ - ("packCount", Method(signature: "packCount()")), - ("getPackData", Method(signature: "getPackData(uint256)")) - ].toTable - ), - Contract(name: "sticker-market", network: Network.Mainnet, address: parseAddress("0x12824271339304d3a9f7e096e62a2a7e73b4a7e7"), - methods: [ - ("buyToken", Method(signature: "buyToken(uint256,address,uint256)")) - ].toTable - ), - newErc721Contract("sticker-pack", Network.Mainnet, parseAddress("0x110101156e8F0743948B2A61aFcf3994A8Fb172e"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]), - # Strikers seems dead. Their website doesn't work anymore - newErc721Contract("strikers", Network.Mainnet, parseAddress("0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"), "STRK", true), - newErc721Contract("ethermon", Network.Mainnet, parseAddress("0xb2c0782ae4a299f7358758b2d15da9bf29e1dd99"), "EMONA", true), - newErc721Contract("kudos", Network.Mainnet, parseAddress("0x2aea4add166ebf38b63d09a75de1a7b94aa24163"), "KDO", true), - newErc721Contract("crypto-kitties", Network.Mainnet, parseAddress("0x06012c8cf97bead5deae237070f9587f8e7a266d"), "CK", true), - Contract(name: "ens-usernames", network: Network.Mainnet, address: parseAddress("0xDB5ac1a559b02E12F29fC0eC0e37Be8E046DEF49"), - methods: [ - ("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")), - ("getPrice", Method(signature: "getPrice()")) - ].toTable - ), - Contract(name: "ens-resolver", network: Network.Mainnet, address: parseAddress("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41"), - methods: [ - ("setPubkey", Method(signature: "setPubkey(bytes32,bytes32,bytes32)")) - ].toTable - ), +var + contracts {.threadvar.}: seq[Contract] + contractsInited {.threadvar.}: bool +proc allContracts(): seq[Contract] = + if contractsInited: + result = contracts + else: + contracts = @[ + # Mainnet contracts + newErc20Contract("Status Network Token", Network.Mainnet, parseAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"), "SNT", 18, true), + newErc20Contract("Dai Stablecoin", Network.Mainnet, parseAddress("0x6b175474e89094c44da98b954eedeac495271d0f"), "DAI", 18, true), + newErc20Contract("Sai Stablecoin v1.0", Network.Mainnet, parseAddress("0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"), "SAI", 18, true), + newErc20Contract("MKR", Network.Mainnet, parseAddress("0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2"), "MKR", 18, true), + newErc20Contract("EOS", Network.Mainnet, parseAddress("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"), "EOS", 18, true), + newErc20Contract("OMGToken", Network.Mainnet, parseAddress("0xd26114cd6ee289accf82350c8d8487fedb8a0c07"), "OMG", 18, true), + newErc20Contract("Populous Platform", Network.Mainnet, parseAddress("0xd4fa1460f537bb9085d22c7bccb5dd450ef28e3a"), "PPT", 8, true), + newErc20Contract("Reputation", Network.Mainnet, parseAddress("0x1985365e9f78359a9b6ad760e32412f4a445e862"), "REP", 18, true), + newErc20Contract("PowerLedger", Network.Mainnet, parseAddress("0x595832f8fc6bf59c85c527fec3740a1b7a361269"), "POWR", 6, true), + newErc20Contract("TenX Pay Token", Network.Mainnet, parseAddress("0xb97048628db6b661d4c2aa833e95dbe1a905b280"), "PAY", 18, true), + newErc20Contract("Veros", Network.Mainnet, parseAddress("0x92e78dae1315067a8819efd6dca432de9dcde2e9"), "VRS", 6, false), + newErc20Contract("Golem Network Token", Network.Mainnet, parseAddress("0xa74476443119a942de498590fe1f2454d7d4ac0d"), "GNT", 18, true), + newErc20Contract("Salt", Network.Mainnet, parseAddress("0x4156d3342d5c385a87d264f90653733592000581"), "SALT", 8, true), + newErc20Contract("BNB", Network.Mainnet, parseAddress("0xb8c77482e45f1f44de1745f52c74426c631bdd52"), "BNB", 18, true), + newErc20Contract("Basic Attention Token", Network.Mainnet, parseAddress("0x0d8775f648430679a709e98d2b0cb6250d2887ef"), "BAT", 18, true), + newErc20Contract("Kyber Network Crystal", Network.Mainnet, parseAddress("0xdd974d5c2e2928dea5f71b9825b8b646686bd200"), "KNC", 18, true), + newErc20Contract("BTU Protocol", Network.Mainnet, parseAddress("0xb683D83a532e2Cb7DFa5275eED3698436371cc9f"), "BTU", 18, true), + newErc20Contract("Digix DAO", Network.Mainnet, parseAddress("0xe0b7927c4af23765cb51314a0e0521a9645f0e2a"), "DGD", 9, true), + newErc20Contract("Aeternity", Network.Mainnet, parseAddress("0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d"), "AE", 18, true), + newErc20Contract("Tronix", Network.Mainnet, parseAddress("0xf230b790e05390fc8295f4d3f60332c93bed42e2"), "TRX", 6, true), + newErc20Contract("Ethos", Network.Mainnet, parseAddress("0x5af2be193a6abca9c8817001f45744777db30756"), "ETHOS", 8, true), + newErc20Contract("Raiden Token", Network.Mainnet, parseAddress("0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6"), "RDN", 18, true), + newErc20Contract("SingularDTV", Network.Mainnet, parseAddress("0xaec2e87e0a235266d9c5adc9deb4b2e29b54d009"), "SNGLS", 0, true), + newErc20Contract("Gnosis Token", Network.Mainnet, parseAddress("0x6810e776880c02933d47db1b9fc05908e5386b96"), "GNO", 18, true), + newErc20Contract("StorjToken", Network.Mainnet, parseAddress("0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac"), "STORJ", 8, true), + newErc20Contract("AdEx", Network.Mainnet, parseAddress("0x4470bb87d77b963a013db939be332f927f2b992e"), "ADX", 4, false), + newErc20Contract("FunFair", Network.Mainnet, parseAddress("0x419d0d8bdd9af5e606ae2232ed285aff190e711b"), "FUN", 8, true), + newErc20Contract("Civic", Network.Mainnet, parseAddress("0x41e5560054824ea6b0732e656e3ad64e20e94e45"), "CVC", 8, true), + newErc20Contract("ICONOMI", Network.Mainnet, parseAddress("0x888666ca69e0f178ded6d75b5726cee99a87d698"), "ICN", 18, true), + newErc20Contract("Walton Token", Network.Mainnet, parseAddress("0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74"), "WTC", 18, true), + newErc20Contract("Bytom", Network.Mainnet, parseAddress("0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750"), "BTM", 8, true), + newErc20Contract("0x Protocol Token", Network.Mainnet, parseAddress("0xe41d2489571d322189246dafa5ebde1f4699f498"), "ZRX", 18, true), + newErc20Contract("Bancor Network Token", Network.Mainnet, parseAddress("0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c"), "BNT", 18, true), + newErc20Contract("Metal", Network.Mainnet, parseAddress("0xf433089366899d83a9f26a773d59ec7ecf30355e"), "MTL", 8, false), + newErc20Contract("PayPie", Network.Mainnet, parseAddress("0xc42209accc14029c1012fb5680d95fbd6036e2a0"), "PPP", 18, true), + newErc20Contract("ChainLink Token", Network.Mainnet, parseAddress("0x514910771af9ca656af840dff83e8264ecf986ca"), "LINK", 18, true), + newErc20Contract("Kin", Network.Mainnet, parseAddress("0x818fc6c2ec5986bc6e2cbf00939d90556ab12ce5"), "KIN", 18, true), + newErc20Contract("Aragon Network Token", Network.Mainnet, parseAddress("0x960b236a07cf122663c4303350609a66a7b288c0"), "ANT", 18, true), + newErc20Contract("MobileGo Token", Network.Mainnet, parseAddress("0x40395044ac3c0c57051906da938b54bd6557f212"), "MGO", 8, true), + newErc20Contract("Monaco", Network.Mainnet, parseAddress("0xb63b606ac810a52cca15e44bb630fd42d8d1d83d"), "MCO", 8, true), + newErc20Contract("loopring", Network.Mainnet, parseAddress("0xef68e7c694f40c8202821edf525de3782458639f"), "LRC", 18, true), + newErc20Contract("Zeus Shield Coin", Network.Mainnet, parseAddress("0x7a41e0517a5eca4fdbc7fbeba4d4c47b9ff6dc63"), "ZSC", 18, true), + newErc20Contract("Streamr DATAcoin", Network.Mainnet, parseAddress("0x0cf0ee63788a0849fe5297f3407f701e122cc023"), "DATA", 18, true), + newErc20Contract("Ripio Credit Network Token", Network.Mainnet, parseAddress("0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6"), "RCN", 18, true), + newErc20Contract("WINGS", Network.Mainnet, parseAddress("0x667088b212ce3d06a1b553a7221e1fd19000d9af"), "WINGS", 18, true), + newErc20Contract("Edgeless", Network.Mainnet, parseAddress("0x08711d3b02c8758f2fb3ab4e80228418a7f8e39c"), "EDG", 0, true), + newErc20Contract("Melon Token", Network.Mainnet, parseAddress("0xbeb9ef514a379b997e0798fdcc901ee474b6d9a1"), "MLN", 18, true), + newErc20Contract("Moeda Loyalty Points", Network.Mainnet, parseAddress("0x51db5ad35c671a87207d88fc11d593ac0c8415bd"), "MDA", 18, true), + newErc20Contract("PILLAR", Network.Mainnet, parseAddress("0xe3818504c1b32bf1557b16c238b2e01fd3149c17"), "PLR", 18, true), + newErc20Contract("QRL", Network.Mainnet, parseAddress("0x697beac28b09e122c4332d163985e8a73121b97f"), "QRL", 8, true), + newErc20Contract("Modum Token", Network.Mainnet, parseAddress("0x957c30ab0426e0c93cd8241e2c60392d08c6ac8e"), "MOD", 0, true), + newErc20Contract("Token-as-a-Service", Network.Mainnet, parseAddress("0xe7775a6e9bcf904eb39da2b68c5efb4f9360e08c"), "TAAS", 6, true), + newErc20Contract("GRID Token", Network.Mainnet, parseAddress("0x12b19d3e2ccc14da04fae33e63652ce469b3f2fd"), "GRID", 12, true), + newErc20Contract("SANtiment network token", Network.Mainnet, parseAddress("0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098"), "SAN", 18, true), + newErc20Contract("SONM Token", Network.Mainnet, parseAddress("0x983f6d60db79ea8ca4eb9968c6aff8cfa04b3c63"), "SNM", 18, true), + newErc20Contract("Request Token", Network.Mainnet, parseAddress("0x8f8221afbb33998d8584a2b05749ba73c37a938a"), "REQ", 18, true), + newErc20Contract("Substratum", Network.Mainnet, parseAddress("0x12480e24eb5bec1a9d4369cab6a80cad3c0a377a"), "SUB", 2, true), + newErc20Contract("Decentraland MANA", Network.Mainnet, parseAddress("0x0f5d2fb29fb7d3cfee444a200298f468908cc942"), "MANA", 18, true), + newErc20Contract("AirSwap Token", Network.Mainnet, parseAddress("0x27054b13b1b798b345b591a4d22e6562d47ea75a"), "AST", 4, true), + newErc20Contract("R token", Network.Mainnet, parseAddress("0x48f775efbe4f5ece6e0df2f7b5932df56823b990"), "R", 0, true), + newErc20Contract("FirstBlood Token", Network.Mainnet, parseAddress("0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7"), "1ST", 18, true), + newErc20Contract("Cofoundit", Network.Mainnet, parseAddress("0x12fef5e57bf45873cd9b62e9dbd7bfb99e32d73e"), "CFI", 18, true), + newErc20Contract("Enigma", Network.Mainnet, parseAddress("0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4"), "ENG", 8, true), + newErc20Contract("Amber Token", Network.Mainnet, parseAddress("0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce"), "AMB", 18, true), + newErc20Contract("XPlay Token", Network.Mainnet, parseAddress("0x90528aeb3a2b736b780fd1b6c478bb7e1d643170"), "XPA", 18, true), + newErc20Contract("Open Trading Network", Network.Mainnet, parseAddress("0x881ef48211982d01e2cb7092c915e647cd40d85c"), "OTN", 18, true), + newErc20Contract("Trustcoin", Network.Mainnet, parseAddress("0xcb94be6f13a1182e4a4b6140cb7bf2025d28e41b"), "TRST", 6, true), + newErc20Contract("Monolith TKN", Network.Mainnet, parseAddress("0xaaaf91d9b90df800df4f55c205fd6989c977e73a"), "TKN", 8, true), + newErc20Contract("RHOC", Network.Mainnet, parseAddress("0x168296bb09e24a88805cb9c33356536b980d3fc5"), "RHOC", 8, true), + newErc20Contract("Target Coin", Network.Mainnet, parseAddress("0xac3da587eac229c9896d919abc235ca4fd7f72c1"), "TGT", 1, false), + newErc20Contract("Everex", Network.Mainnet, parseAddress("0xf3db5fa2c66b7af3eb0c0b782510816cbe4813b8"), "EVX", 4, true), + newErc20Contract("ICOS", Network.Mainnet, parseAddress("0x014b50466590340d41307cc54dcee990c8d58aa8"), "ICOS", 6, true), + newErc20Contract("district0x Network Token", Network.Mainnet, parseAddress("0x0abdace70d3790235af448c88547603b945604ea"), "DNT", 18, true), + newErc20Contract("Dentacoin", Network.Mainnet, parseAddress("0x08d32b0da63e2c3bcf8019c9c5d849d7a9d791e6"), "٨", 0, false), + newErc20Contract("Eidoo Token", Network.Mainnet, parseAddress("0xced4e93198734ddaff8492d525bd258d49eb388e"), "EDO", 18, true), + newErc20Contract("BitDice", Network.Mainnet, parseAddress("0x29d75277ac7f0335b2165d0895e8725cbf658d73"), "CSNO", 8, false), + newErc20Contract("Cobinhood Token", Network.Mainnet, parseAddress("0xb2f7eb1f2c37645be61d73953035360e768d81e6"), "COB", 18, true), + newErc20Contract("Enjin Coin", Network.Mainnet, parseAddress("0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c"), "ENJ", 18, false), + newErc20Contract("AVENTUS", Network.Mainnet, parseAddress("0x0d88ed6e74bbfd96b831231638b66c05571e824f"), "AVT", 18, false), + newErc20Contract("Chronobank TIME", Network.Mainnet, parseAddress("0x6531f133e6deebe7f2dce5a0441aa7ef330b4e53"), "TIME", 8, false), + newErc20Contract("Cindicator Token", Network.Mainnet, parseAddress("0xd4c435f5b09f855c3317c8524cb1f586e42795fa"), "CND", 18, true), + newErc20Contract("Stox", Network.Mainnet, parseAddress("0x006bea43baa3f7a6f765f14f10a1a1b08334ef45"), "STX", 18, true), + newErc20Contract("Xaurum", Network.Mainnet, parseAddress("0x4df812f6064def1e5e029f1ca858777cc98d2d81"), "XAUR", 8, true), + newErc20Contract("Vibe", Network.Mainnet, parseAddress("0x2c974b2d0ba1716e644c1fc59982a89ddd2ff724"), "VIB", 18, true), + newErc20Contract("PRG", Network.Mainnet, parseAddress("0x7728dfef5abd468669eb7f9b48a7f70a501ed29d"), "PRG", 6, false), + newErc20Contract("Delphy Token", Network.Mainnet, parseAddress("0x6c2adc2073994fb2ccc5032cc2906fa221e9b391"), "DPY", 18, true), + newErc20Contract("CoinDash Token", Network.Mainnet, parseAddress("0x2fe6ab85ebbf7776fee46d191ee4cea322cecf51"), "CDT", 18, true), + newErc20Contract("Tierion Network Token", Network.Mainnet, parseAddress("0x08f5a9235b08173b7569f83645d2c7fb55e8ccd8"), "TNT", 8, true), + newErc20Contract("DomRaiderToken", Network.Mainnet, parseAddress("0x9af4f26941677c706cfecf6d3379ff01bb85d5ab"), "DRT", 8, true), + newErc20Contract("SPANK", Network.Mainnet, parseAddress("0x42d6622dece394b54999fbd73d108123806f6a18"), "SPANK", 18, true), + newErc20Contract("Berlin Coin", Network.Mainnet, parseAddress("0x80046305aaab08f6033b56a360c184391165dc2d"), "BRLN", 18, true), + newErc20Contract("USD//C", Network.Mainnet, parseAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), "USDC", 6, true), + newErc20Contract("Livepeer Token", Network.Mainnet, parseAddress("0x58b6a8a3302369daec383334672404ee733ab239"), "LPT", 18, true), + newErc20Contract("Simple Token", Network.Mainnet, parseAddress("0x2c4e8f2d746113d0696ce89b35f0d8bf88e0aeca"), "ST", 18, true), + newErc20Contract("Wrapped BTC", Network.Mainnet, parseAddress("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"), "WBTC", 8, true), + newErc20Contract("Bloom Token", Network.Mainnet, parseAddress("0x107c4504cd79c5d2696ea0030a8dd4e92601b82e"), "BLT", 18, true), + Contract(name: "stickers", network: Network.Mainnet, address: parseAddress("0x0577215622f43a39f4bc9640806dfea9b10d2a36"), + methods: [ + ("packCount", Method(signature: "packCount()")), + ("getPackData", Method(signature: "getPackData(uint256)")) + ].toTable + ), + Contract(name: "sticker-market", network: Network.Mainnet, address: parseAddress("0x12824271339304d3a9f7e096e62a2a7e73b4a7e7"), + methods: [ + ("buyToken", Method(signature: "buyToken(uint256,address,uint256)")) + ].toTable + ), + newErc721Contract("sticker-pack", Network.Mainnet, parseAddress("0x110101156e8F0743948B2A61aFcf3994A8Fb172e"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]), + # Strikers seems dead. Their website doesn't work anymore + newErc721Contract("strikers", Network.Mainnet, parseAddress("0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"), "STRK", true), + newErc721Contract("ethermon", Network.Mainnet, parseAddress("0xb2c0782ae4a299f7358758b2d15da9bf29e1dd99"), "EMONA", true), + newErc721Contract("kudos", Network.Mainnet, parseAddress("0x2aea4add166ebf38b63d09a75de1a7b94aa24163"), "KDO", true), + newErc721Contract("crypto-kitties", Network.Mainnet, parseAddress("0x06012c8cf97bead5deae237070f9587f8e7a266d"), "CK", true), + Contract(name: "ens-usernames", network: Network.Mainnet, address: parseAddress("0xDB5ac1a559b02E12F29fC0eC0e37Be8E046DEF49"), + methods: [ + ("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")), + ("getPrice", Method(signature: "getPrice()")) + ].toTable + ), + Contract(name: "ens-resolver", network: Network.Mainnet, address: parseAddress("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41"), + methods: [ + ("setPubkey", Method(signature: "setPubkey(bytes32,bytes32,bytes32)")) + ].toTable + ), - # Testnet (Ropsten) contracts - newErc20Contract("Status Test Token", Network.Testnet, parseAddress("0xc55cf4b03948d7ebc8b9e8bad92643703811d162"), "STT", 18, true), - newErc20Contract("Handy Test Token", Network.Testnet, parseAddress("0xdee43a267e8726efd60c2e7d5b81552dcd4fa35c"), "HND", 0, false), - newErc20Contract("Lucky Test Token", Network.Testnet, parseAddress("0x703d7dc0bc8e314d65436adf985dda51e09ad43b"), "LXS", 2, false), - newErc20Contract("Adi Test Token", Network.Testnet, parseAddress("0xe639e24346d646e927f323558e6e0031bfc93581"), "ADI", 7, false), - newErc20Contract("Wagner Test Token", Network.Testnet, parseAddress("0x2e7cd05f437eb256f363417fd8f920e2efa77540"), "WGN", 10, false), - newErc20Contract("Modest Test Token", Network.Testnet, parseAddress("0x57cc9b83730e6d22b224e9dc3e370967b44a2de0"), "MDS", 18, false), - Contract(name: "tribute-to-talk", network: Network.Testnet, address: parseAddress("0xC61aa0287247a0398589a66fCD6146EC0F295432")), - Contract(name: "stickers", network: Network.Testnet, address: parseAddress("0x8cc272396be7583c65bee82cd7b743c69a87287d"), - methods: [ - ("packCount", Method(signature: "packCount()")), - ("getPackData", Method(signature: "getPackData(uint256)")) - ].toTable - ), - Contract(name: "sticker-market", network: Network.Testnet, address: parseAddress("0x6CC7274aF9cE9572d22DFD8545Fb8c9C9Bcb48AD"), - methods: [ - ("buyToken", Method(signature: "buyToken(uint256,address,uint256)")) - ].toTable - ), - newErc721Contract("sticker-pack", Network.Testnet, parseAddress("0xf852198d0385c4b871e0b91804ecd47c6ba97351"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]), - newErc721Contract("kudos", Network.Testnet, parseAddress("0xcd520707fc68d153283d518b29ada466f9091ea8"), "KDO", true), - Contract(name: "ens-usernames", network: Network.Testnet, address: parseAddress("0x11d9F481effd20D76cEE832559bd9Aca25405841"), - methods: [ - ("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")), - ("getPrice", Method(signature: "getPrice()")) - ].toTable - ), - Contract(name: "ens-resolver", network: Network.Testnet, address: parseAddress("0x42D63ae25990889E35F215bC95884039Ba354115"), - methods: [ - ("setPubkey", Method(signature: "setPubkey(bytes32,bytes32,bytes32)")) - ].toTable - ), + # Testnet (Ropsten) contracts + newErc20Contract("Status Test Token", Network.Testnet, parseAddress("0xc55cf4b03948d7ebc8b9e8bad92643703811d162"), "STT", 18, true), + newErc20Contract("Handy Test Token", Network.Testnet, parseAddress("0xdee43a267e8726efd60c2e7d5b81552dcd4fa35c"), "HND", 0, false), + newErc20Contract("Lucky Test Token", Network.Testnet, parseAddress("0x703d7dc0bc8e314d65436adf985dda51e09ad43b"), "LXS", 2, false), + newErc20Contract("Adi Test Token", Network.Testnet, parseAddress("0xe639e24346d646e927f323558e6e0031bfc93581"), "ADI", 7, false), + newErc20Contract("Wagner Test Token", Network.Testnet, parseAddress("0x2e7cd05f437eb256f363417fd8f920e2efa77540"), "WGN", 10, false), + newErc20Contract("Modest Test Token", Network.Testnet, parseAddress("0x57cc9b83730e6d22b224e9dc3e370967b44a2de0"), "MDS", 18, false), + Contract(name: "tribute-to-talk", network: Network.Testnet, address: parseAddress("0xC61aa0287247a0398589a66fCD6146EC0F295432")), + Contract(name: "stickers", network: Network.Testnet, address: parseAddress("0x8cc272396be7583c65bee82cd7b743c69a87287d"), + methods: [ + ("packCount", Method(signature: "packCount()")), + ("getPackData", Method(signature: "getPackData(uint256)")) + ].toTable + ), + Contract(name: "sticker-market", network: Network.Testnet, address: parseAddress("0x6CC7274aF9cE9572d22DFD8545Fb8c9C9Bcb48AD"), + methods: [ + ("buyToken", Method(signature: "buyToken(uint256,address,uint256)")) + ].toTable + ), + newErc721Contract("sticker-pack", Network.Testnet, parseAddress("0xf852198d0385c4b871e0b91804ecd47c6ba97351"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]), + newErc721Contract("kudos", Network.Testnet, parseAddress("0xcd520707fc68d153283d518b29ada466f9091ea8"), "KDO", true), + Contract(name: "ens-usernames", network: Network.Testnet, address: parseAddress("0x11d9F481effd20D76cEE832559bd9Aca25405841"), + methods: [ + ("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")), + ("getPrice", Method(signature: "getPrice()")) + ].toTable + ), + Contract(name: "ens-resolver", network: Network.Testnet, address: parseAddress("0x42D63ae25990889E35F215bC95884039Ba354115"), + methods: [ + ("setPubkey", Method(signature: "setPubkey(bytes32,bytes32,bytes32)")) + ].toTable + ), - # Rinkeby contracts - newErc20Contract("Moksha Coin", Network.Rinkeby, parseAddress("0x6ba7dc8dd10880ab83041e60c4ede52bb607864b"), "MOKSHA", 18, false), - newErc20Contract("WIBB", Network.Rinkeby, parseAddress("0x7d4ccf6af2f0fdad48ee7958bcc28bdef7b732c7"), "WIBB", 18, false), - newErc20Contract("Status Test Token", Network.Rinkeby, parseAddress("0x43d5adc3b49130a575ae6e4b00dfa4bc55c71621"), "STT", 18, false), + # Rinkeby contracts + newErc20Contract("Moksha Coin", Network.Rinkeby, parseAddress("0x6ba7dc8dd10880ab83041e60c4ede52bb607864b"), "MOKSHA", 18, false), + newErc20Contract("WIBB", Network.Rinkeby, parseAddress("0x7d4ccf6af2f0fdad48ee7958bcc28bdef7b732c7"), "WIBB", 18, false), + newErc20Contract("Status Test Token", Network.Rinkeby, parseAddress("0x43d5adc3b49130a575ae6e4b00dfa4bc55c71621"), "STT", 18, false), - # xDai contracts - newErc20Contract("buffiDai", Network.XDai, parseAddress("0x3e50bf6703fc132a94e4baff068db2055655f11b"), "BUFF", 18, false), + # xDai contracts + newErc20Contract("buffiDai", Network.XDai, parseAddress("0x3e50bf6703fc132a94e4baff068db2055655f11b"), "BUFF", 18, false), - newErc20Contract("Uniswap", Network.Mainnet, parseAddress("0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"), "UNI", 18, true), - newErc20Contract("Compound", Network.Mainnet, parseAddress("0xc00e94cb662c3520282e6f5717214004a7f26888"), "COMP", 18, true), - newErc20Contract("Balancer", Network.Mainnet, parseAddress("0xba100000625a3754423978a60c9317c58a424e3d"), "BAL", 18, true), - newErc20Contract("Akropolis", Network.Mainnet, parseAddress("0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7"), "AKRO", 18, true), - newErc20Contract("Orchid", Network.Mainnet, parseAddress("0x4575f41308EC1483f3d399aa9a2826d74Da13Deb"), "OXT", 18, false), -] + newErc20Contract("Uniswap", Network.Mainnet, parseAddress("0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"), "UNI", 18, true), + newErc20Contract("Compound", Network.Mainnet, parseAddress("0xc00e94cb662c3520282e6f5717214004a7f26888"), "COMP", 18, true), + newErc20Contract("Balancer", Network.Mainnet, parseAddress("0xba100000625a3754423978a60c9317c58a424e3d"), "BAL", 18, true), + newErc20Contract("Akropolis", Network.Mainnet, parseAddress("0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7"), "AKRO", 18, true), + newErc20Contract("Orchid", Network.Mainnet, parseAddress("0x4575f41308EC1483f3d399aa9a2826d74Da13Deb"), "OXT", 18, false), + ] + contractsInited = true + result = contracts proc getContract(network: Network, name: string): Contract = - {.gcsafe.}: - withLock contractsLock: - let found = ALL_CONTRACTS.filter(contract => contract.name == name and contract.network == network) - result = if found.len > 0: found[0] else: nil + let found = allContracts().filter(contract => contract.name == name and contract.network == network) + result = if found.len > 0: found[0] else: nil proc getContract*(name: string): Contract = let network = settings.getCurrentNetwork() @@ -275,27 +279,19 @@ proc getErc20ContractByAddress*(contracts: seq[Erc20Contract], address: Address) proc getErc20Contract*(symbol: string): Erc20Contract = let network = settings.getCurrentNetwork() - {.gcsafe.}: - withLock contractsLock: - result = ALL_CONTRACTS.filter(contract => contract.network == network and contract of Erc20Contract).map(contract => Erc20Contract(contract)).getErc20ContractBySymbol(symbol) + result = allContracts().filter(contract => contract.network == network and contract of Erc20Contract).map(contract => Erc20Contract(contract)).getErc20ContractBySymbol(symbol) proc getErc20Contract*(address: Address): Erc20Contract = let network = settings.getCurrentNetwork() - {.gcsafe.}: - withLock contractsLock: - result = ALL_CONTRACTS.filter(contract => contract.network == network and contract of Erc20Contract).map(contract => Erc20Contract(contract)).getErc20ContractByAddress(address) + result = allContracts().filter(contract => contract.network == network and contract of Erc20Contract).map(contract => Erc20Contract(contract)).getErc20ContractByAddress(address) proc getErc20Contracts*(): seq[Erc20Contract] = let network = settings.getCurrentNetwork() - {.gcsafe.}: - withLock contractsLock: - result = ALL_CONTRACTS.filter(contract => contract of Erc20Contract and contract.network == network).map(contract => Erc20Contract(contract)) + result = allContracts().filter(contract => contract of Erc20Contract and contract.network == network).map(contract => Erc20Contract(contract)) proc getErc721Contract(network: Network, name: string): Erc721Contract = - {.gcsafe.}: - withLock contractsLock: - let found = ALL_CONTRACTS.filter(contract => contract of Erc721Contract and Erc721Contract(contract).name.toLower == name.toLower and contract.network == network) - result = if found.len > 0: Erc721Contract(found[0]) else: nil + let found = allContracts().filter(contract => contract of Erc721Contract and Erc721Contract(contract).name.toLower == name.toLower and contract.network == network) + result = if found.len > 0: Erc721Contract(found[0]) else: nil proc getErc721Contract*(name: string): Erc721Contract = let network = settings.getCurrentNetwork() @@ -303,9 +299,7 @@ proc getErc721Contract*(name: string): Erc721Contract = proc getErc721Contracts*(): seq[Erc721Contract] = let network = settings.getCurrentNetwork() - {.gcsafe.}: - withLock contractsLock: - result = ALL_CONTRACTS.filter(contract => contract of Erc721Contract and contract.network == network).map(contract => Erc721Contract(contract)) + result = allContracts().filter(contract => contract of Erc721Contract and contract.network == network).map(contract => Erc721Contract(contract)) proc getSntContract*(): Erc20Contract = if settings.getCurrentNetwork() == Network.Mainnet: diff --git a/src/status/libstatus/settings.nim b/src/status/libstatus/settings.nim index 7b29953f21..abf3c34905 100644 --- a/src/status/libstatus/settings.nim +++ b/src/status/libstatus/settings.nim @@ -1,37 +1,46 @@ -import core, ./types, ../signals/types as statusgo_types, ./accounts/constants, ./utils -import json, tables, sugar, sequtils, strutils -import json_serialization -import locks -import uuids +import + json, tables, sugar, sequtils, strutils, atomics -var settingsLock {.global.}: Lock -initLock(settingsLock) +import + json_serialization, chronicles, uuids -var settings = %*{} -var dirty = true +import + ./core, ./types, ../signals/types as statusgo_types, ./accounts/constants, + ./utils + +var + settings {.threadvar.}: JsonNode + settingsInited {.threadvar.}: bool + dirty: Atomic[bool] + +dirty.store(true) +settings = %* {} proc saveSetting*(key: Setting, value: string | JsonNode): StatusGoError = - withLock settingsLock: - try: - let response = callPrivateRPC("settings_saveSetting", %* [key, value]) - result = Json.decode($response, StatusGoError) - except: - dirty = true + try: + let response = callPrivateRPC("settings_saveSetting", %* [key, value]) + let responseResult = $(response.parseJSON(){"result"}) + if responseResult == "null": + result.error = "" + else: result = Json.decode(response, StatusGoError) + except Exception as e: + error "Error saving setting", key=key, value=value, msg=e.msg + dirty.store(true) proc getWeb3ClientVersion*(): string = parseJson(callPrivateRPC("web3_clientVersion"))["result"].getStr proc getSettings*(useCached: bool = true, keepSensitiveData: bool = false): JsonNode = - withLock settingsLock: - {.gcsafe.}: - if useCached and not dirty and not keepSensitiveData: - result = settings - else: - result = callPrivateRPC("settings_getSettings").parseJSON()["result"] - if not keepSensitiveData: - dirty = false - delete(result, "mnemonic") - settings = result + let cacheIsDirty = (not settingsInited) or dirty.load + if useCached and (not cacheIsDirty) and (not keepSensitiveData): + result = settings + else: + result = callPrivateRPC("settings_getSettings").parseJSON()["result"] + if not keepSensitiveData: + dirty.store(false) + delete(result, "mnemonic") + settings = result + settingsInited = true proc getSetting*[T](name: Setting, defaultValue: T, useCached: bool = true): T = let settings: JsonNode = getSettings(useCached, $name == "mnemonic") diff --git a/src/status/libstatus/stickers.nim b/src/status/libstatus/stickers.nim index 2aba83304a..52bf22abe1 100644 --- a/src/status/libstatus/stickers.nim +++ b/src/status/libstatus/stickers.nim @@ -47,7 +47,8 @@ proc decodeContentHash*(value: string): string = # 20 = multihash length = 32 # ...rest = multihash digest let multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get() - result = $Cid.init(CIDv0, MultiCodec.codec(codec), multiHash) + let resultTyped = Cid.init(CIDv0, MultiCodec.codec(codec), multiHash).get() + result = $resultTyped trace "Decoded sticker hash", cid=result except Exception as e: error "Error decoding sticker", hash=value, exception=e.msg @@ -59,14 +60,14 @@ proc decodeContentHash*(value: string): string = proc getBalance*(address: Address): int = let contract = contracts.getContract("sticker-pack") if contract == nil: return 0 - + let balanceOf = BalanceOf(address: address) payload = %* [{ "to": $contract.address, "data": contract.methods["balanceOf"].encodeAbi(balanceOf) }, "latest"] - + let responseStr = status.callPrivateRPC("eth_call", payload) let response = Json.decode(responseStr, RpcResponse) if not response.error.isNil: @@ -84,7 +85,7 @@ proc getPackCount*(): int = "to": $contract.address, "data": contract.methods["packCount"].encodeAbi() }, "latest"] - + let responseStr = status.callPrivateRPC("eth_call", payload) let response = Json.decode(responseStr, RpcResponse) if not response.error.isNil: @@ -95,39 +96,38 @@ proc getPackCount*(): int = # Gets sticker pack data proc getPackData*(id: Stuint[256]): StickerPack = - {.gcsafe.}: - let - contract = contracts.getContract("stickers") - contractMethod = contract.methods["getPackData"] - getPackData = GetPackData(packId: id) - payload = %* [{ - "to": $contract.address, - "data": contractMethod.encodeAbi(getPackData) - }, "latest"] - let responseStr = status.callPrivateRPC("eth_call", payload) - let response = Json.decode(responseStr, RpcResponse) - if not response.error.isNil: - raise newException(RpcException, "Error getting sticker pack data: " & response.error.message) + let + contract = contracts.getContract("stickers") + contractMethod = contract.methods["getPackData"] + getPackData = GetPackData(packId: id) + payload = %* [{ + "to": $contract.address, + "data": contractMethod.encodeAbi(getPackData) + }, "latest"] + let responseStr = status.callPrivateRPC("eth_call", payload) + let response = Json.decode(responseStr, RpcResponse) + if not response.error.isNil: + raise newException(RpcException, "Error getting sticker pack data: " & response.error.message) - let packData = contracts.decodeContractResponse[PackData](response.result) + let packData = contracts.decodeContractResponse[PackData](response.result) - # contract response includes a contenthash, which needs to be decoded to reveal - # an IPFS identifier. Once decoded, download the content from IPFS. This content - # is in EDN format, ie https://ipfs.infura.io/ipfs/QmWVVLwVKCwkVNjYJrRzQWREVvEk917PhbHYAUhA1gECTM - # and it also needs to be decoded in to a nim type - let secureSSLContext = newContext() - let client = newHttpClient(sslContext = secureSSLContext) - let contentHash = contracts.toHex(packData.contentHash) - let url = "https://ipfs.infura.io/ipfs/" & decodeContentHash(contentHash) - var ednMeta = client.getContent(url) + # contract response includes a contenthash, which needs to be decoded to reveal + # an IPFS identifier. Once decoded, download the content from IPFS. This content + # is in EDN format, ie https://ipfs.infura.io/ipfs/QmWVVLwVKCwkVNjYJrRzQWREVvEk917PhbHYAUhA1gECTM + # and it also needs to be decoded in to a nim type + let secureSSLContext = newContext() + let client = newHttpClient(sslContext = secureSSLContext) + let contentHash = contracts.toHex(packData.contentHash) + let url = "https://ipfs.infura.io/ipfs/" & decodeContentHash(contentHash) + var ednMeta = client.getContent(url) - # decode the EDN content in to a StickerPack - result = edn_helpers.decode[StickerPack](ednMeta) - # EDN doesn't include a packId for each sticker, so add it here - result.stickers.apply(proc(sticker: var Sticker) = - sticker.packId = truncate(id, int)) - result.id = truncate(id, int) - result.price = packData.price + # decode the EDN content in to a StickerPack + result = edn_helpers.decode[StickerPack](ednMeta) + # EDN doesn't include a packId for each sticker, so add it here + result.stickers.apply(proc(sticker: var Sticker) = + sticker.packId = truncate(id, int)) + result.id = truncate(id, int) + result.price = packData.price proc tokenOfOwnerByIndex*(address: Address, idx: Stuint[256]): int = let @@ -137,7 +137,7 @@ proc tokenOfOwnerByIndex*(address: Address, idx: Stuint[256]): int = "to": $contract.address, "data": contract.methods["tokenOfOwnerByIndex"].encodeAbi(tokenOfOwnerByIndex) }, "latest"] - + let responseStr = status.callPrivateRPC("eth_call", payload) let response = Json.decode(responseStr, RpcResponse) if not response.error.isNil: @@ -154,7 +154,7 @@ proc getPackIdFromTokenId*(tokenId: Stuint[256]): int = "to": $contract.address, "data": contract.methods["tokenPackId"].encodeAbi(tokenPackId) }, "latest"] - + let responseStr = status.callPrivateRPC("eth_call", payload) let response = Json.decode(responseStr, RpcResponse) if not response.error.isNil: @@ -202,7 +202,7 @@ proc getRecentStickers*(): seq[Sticker] = proc getAvailableStickerPacks*(): Table[int, StickerPack] = var availableStickerPacks = initTable[int, StickerPack]() - try: + try: let numPacks = getPackCount() for i in 0.. 0: + info "[threadpool] removing from taskQueue", + newlength=(taskQueue.len - 1) + task = taskQueue[0] + taskQueue.delete 0, 0 + + info "[threadpool] removing from threadsIdle", + newlength=(threadsIdle.len - 1) + let tpl = threadsIdle[0] + threadsIdle.delete 0, 0 + info "[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) + + else: + error "[threadpool] unknown notification", notice=notification.notice + except Exception as e: + warn "[threadpool] unknown error in thread notification", message=task, error=e.msg + + 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", + newlength=(taskQueue.len + 1) + taskQueue.add task + + # do we have available threads in the threadpool? + elif threadsBusy.len < arg.size: + # check if we have tasks waiting on queue + if taskQueue.len > 0: + # remove first element from the task queue + info "[threadpool] adding to taskQueue", + newlength=(taskQueue.len + 1) + taskQueue.add task + info "[threadpool] removing from taskQueue", + newlength=(taskQueue.len - 1) + task = taskQueue[0] + taskQueue.delete 0, 0 + + info "[threadpool] removing from threadsIdle", + newlength=(threadsIdle.len - 1) + let tpl = threadsIdle[0] + threadsIdle.delete 0, 0 + info "[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: + tpl.chanSendToTask.close() + allTaskThreads.add tpl.thr + for tpl in threadsBusy.values: + tpl.chanSendToTask.close() + allTaskThreads.add tpl.thr + + chanSendToMain.close() + chanRecvFromMainOrTask.close() + + joinThreads(allTaskThreads) + +proc poolThread(arg: PoolThreadArg) {.thread.} = + waitFor pool(arg) diff --git a/src/status/wallet.nim b/src/status/wallet.nim index 01a5d2c48d..fe5ce3e812 100644 --- a/src/status/wallet.nim +++ b/src/status/wallet.nim @@ -349,3 +349,18 @@ proc getGasPricePredictions*(self: WalletModel): GasPricePrediction = except Exception as e: echo "error getting gas price predictions" echo e.msg + +proc getGasPricePredictions2*(): 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 diff --git a/vendor/DOtherSide b/vendor/DOtherSide index 1cc16aaa5c..5177a129d4 160000 --- a/vendor/DOtherSide +++ b/vendor/DOtherSide @@ -1 +1 @@ -Subproject commit 1cc16aaa5c643d0d33c31f9953fbe4a9f6bde151 +Subproject commit 5177a129d45985bb61471d7afecd397a82ed9d42 diff --git a/vendor/edn.nim b/vendor/edn.nim index 4cda60880e..3305e41f9d 160000 --- a/vendor/edn.nim +++ b/vendor/edn.nim @@ -1 +1 @@ -Subproject commit 4cda60880e108f0cb7efe1c209308f2db03267d9 +Subproject commit 3305e41f9da3f2f21c56bd23b74b0a3589f3bf3e diff --git a/vendor/nim-libp2p b/vendor/nim-libp2p index 8d9e231a74..70deac9e0d 160000 --- a/vendor/nim-libp2p +++ b/vendor/nim-libp2p @@ -1 +1 @@ -Subproject commit 8d9e231a74c1afc76b6745e05020f8d4e33501e7 +Subproject commit 70deac9e0d16f7d00a0d4404ed191042dbf079be diff --git a/vendor/nim-task-runner b/vendor/nim-task-runner new file mode 160000 index 0000000000..a87f3f85be --- /dev/null +++ b/vendor/nim-task-runner @@ -0,0 +1 @@ +Subproject commit a87f3f85be052fb3332358f95079a059cf1daf15