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`.
This commit is contained in:
parent
6b6a318a8c
commit
66912fd811
|
@ -4,6 +4,7 @@ noBackup/
|
||||||
*.pro.user
|
*.pro.user
|
||||||
*.pro.autosave
|
*.pro.autosave
|
||||||
*.qml.autosave
|
*.qml.autosave
|
||||||
|
.update.timestamp
|
||||||
.vscode
|
.vscode
|
||||||
bin/
|
bin/
|
||||||
/bottles/
|
/bottles/
|
||||||
|
|
|
@ -91,3 +91,9 @@
|
||||||
[submodule "vendor/nim-status-go"]
|
[submodule "vendor/nim-status-go"]
|
||||||
path = vendor/nim-status-go
|
path = vendor/nim-status-go
|
||||||
url = https://github.com/status-im/nim-status-go.git
|
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
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ../../../status/libstatus/stickers as status_stickers
|
||||||
import ../../../status/libstatus/wallet as status_wallet
|
import ../../../status/libstatus/wallet as status_wallet
|
||||||
import sticker_pack_list, sticker_list, chat_item
|
import sticker_pack_list, sticker_list, chat_item
|
||||||
import json_serialization
|
import json_serialization
|
||||||
|
import ../../../status/tasks/task_manager
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "stickers-view"
|
topics = "stickers-view"
|
||||||
|
@ -30,10 +31,10 @@ QtObject:
|
||||||
result.recentStickers = newStickerList()
|
result.recentStickers = newStickerList()
|
||||||
result.activeChannel = activeChannel
|
result.activeChannel = activeChannel
|
||||||
result.setup
|
result.setup
|
||||||
|
|
||||||
proc addStickerPackToList*(self: StickersView, stickerPack: StickerPack, isInstalled, isBought, isPending: bool) =
|
proc addStickerPackToList*(self: StickersView, stickerPack: StickerPack, isInstalled, isBought, isPending: bool) =
|
||||||
self.stickerPacks.addStickerPackToList(stickerPack, newStickerList(stickerPack.stickers), isInstalled, isBought, isPending)
|
self.stickerPacks.addStickerPackToList(stickerPack, newStickerList(stickerPack.stickers), isInstalled, isBought, isPending)
|
||||||
|
|
||||||
proc getStickerPackList(self: StickersView): QVariant {.slot.} =
|
proc getStickerPackList(self: StickersView): QVariant {.slot.} =
|
||||||
newQVariant(self.stickerPacks)
|
newQVariant(self.stickerPacks)
|
||||||
|
|
||||||
|
@ -41,19 +42,12 @@ QtObject:
|
||||||
read = getStickerPackList
|
read = getStickerPackList
|
||||||
|
|
||||||
proc transactionWasSent*(self: StickersView, txResult: string) {.signal.}
|
proc transactionWasSent*(self: StickersView, txResult: string) {.signal.}
|
||||||
|
|
||||||
proc transactionCompleted*(self: StickersView, success: bool, txHash: string, revertReason: 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.} =
|
proc estimate*(self: StickersView, packId: int, address: string, price: string, uuid: string) {.slot.} =
|
||||||
let status_stickers = self.status.stickers
|
self.status.taskManager.threadPool.stickers.stickerPackPurchaseGasEstimate(cast[pointer](self.vptr), "setGasEstimate", packId, address, price, uuid)
|
||||||
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)
|
|
||||||
|
|
||||||
proc gasEstimateReturned*(self: StickersView, estimate: int, uuid: string) {.signal.}
|
proc gasEstimateReturned*(self: StickersView, estimate: int, uuid: string) {.signal.}
|
||||||
|
|
||||||
proc setGasEstimate*(self: StickersView, estimateJson: string) {.slot.} =
|
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.} =
|
proc buy*(self: StickersView, packId: int, address: string, price: string, gas: string, gasPrice: string, password: string): string {.slot.} =
|
||||||
var success: bool
|
var success: bool
|
||||||
let response = self.status.stickers.buyPack(packId, address, price, gas, gasPrice, password, success)
|
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
|
# check if response["error"] is not null and handle the error
|
||||||
result = $(%* { "result": %response, "success": %success })
|
result = $(%* { "result": %response, "success": %success })
|
||||||
if success:
|
if success:
|
||||||
|
@ -133,7 +127,7 @@ QtObject:
|
||||||
|
|
||||||
proc resetBuyAttempt*(self: StickersView, packId: int) {.slot.} =
|
proc resetBuyAttempt*(self: StickersView, packId: int) {.slot.} =
|
||||||
self.stickerPacks.updateStickerPackInList(packId, false, false)
|
self.stickerPacks.updateStickerPackInList(packId, false, false)
|
||||||
|
|
||||||
proc uninstall*(self: StickersView, packId: int) {.slot.} =
|
proc uninstall*(self: StickersView, packId: int) {.slot.} =
|
||||||
self.status.stickers.uninstallStickerPack(packId)
|
self.status.stickers.uninstallStickerPack(packId)
|
||||||
self.status.stickers.removeRecentStickers(packId)
|
self.status.stickers.removeRecentStickers(packId)
|
||||||
|
@ -156,4 +150,4 @@ QtObject:
|
||||||
proc send*(self: StickersView, hash: string, pack: int) {.slot.} =
|
proc send*(self: StickersView, hash: string, pack: int) {.slot.} =
|
||||||
let sticker = Sticker(hash: hash, packId: pack)
|
let sticker = Sticker(hash: hash, packId: pack)
|
||||||
self.addRecentStickerToList(sticker)
|
self.addRecentStickerToList(sticker)
|
||||||
self.status.chat.sendSticker(self.activeChannel.id, sticker)
|
self.status.chat.sendSticker(self.activeChannel.id, sticker)
|
||||||
|
|
|
@ -491,9 +491,8 @@ QtObject:
|
||||||
proc gasPricePredictionsChanged*(self: WalletView) {.signal.}
|
proc gasPricePredictionsChanged*(self: WalletView) {.signal.}
|
||||||
|
|
||||||
proc getGasPricePredictions*(self: WalletView) {.slot.} =
|
proc getGasPricePredictions*(self: WalletView) {.slot.} =
|
||||||
let walletModel = self.status.wallet
|
|
||||||
spawnAndSend(self, "getGasPricePredictionsResult") do:
|
spawnAndSend(self, "getGasPricePredictionsResult") do:
|
||||||
$ %walletModel.getGasPricePredictions()
|
$ %getGasPricePredictions2()
|
||||||
|
|
||||||
proc getGasPricePredictionsResult(self: WalletView, gasPricePredictionsJson: string) {.slot.} =
|
proc getGasPricePredictionsResult(self: WalletView, gasPricePredictionsJson: string) {.slot.} =
|
||||||
let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction)
|
let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction)
|
||||||
|
|
|
@ -10,11 +10,13 @@ import app/onboarding/core as onboarding
|
||||||
import app/login/core as login
|
import app/login/core as login
|
||||||
import app/provider/core as provider
|
import app/provider/core as provider
|
||||||
import status/signals/core as signals
|
import status/signals/core as signals
|
||||||
|
import status/tasks/task_manager
|
||||||
import status/libstatus/types
|
import status/libstatus/types
|
||||||
import status/libstatus/accounts/constants
|
import status/libstatus/accounts/constants
|
||||||
import status_go
|
import status_go
|
||||||
import status/status as statuslib
|
import status/status as statuslib
|
||||||
import ./eventemitter
|
import ./eventemitter
|
||||||
|
import chronos, task_runner
|
||||||
|
|
||||||
var signalsQObjPointer: pointer
|
var signalsQObjPointer: pointer
|
||||||
|
|
||||||
|
@ -28,7 +30,9 @@ proc mainProc() =
|
||||||
else:
|
else:
|
||||||
"/../fleets.json"
|
"/../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()
|
status.initNode()
|
||||||
|
|
||||||
enableHDPI()
|
enableHDPI()
|
||||||
|
@ -132,6 +136,7 @@ proc mainProc() =
|
||||||
wallet.checkPendingTransactions()
|
wallet.checkPendingTransactions()
|
||||||
wallet.start()
|
wallet.start()
|
||||||
|
|
||||||
|
|
||||||
engine.setRootContextProperty("loginModel", login.variant)
|
engine.setRootContextProperty("loginModel", login.variant)
|
||||||
engine.setRootContextProperty("onboardingModel", onboarding.variant)
|
engine.setRootContextProperty("onboardingModel", onboarding.variant)
|
||||||
|
|
||||||
|
@ -152,6 +157,7 @@ proc mainProc() =
|
||||||
profile.delete()
|
profile.delete()
|
||||||
utilsController.delete()
|
utilsController.delete()
|
||||||
browserController.delete()
|
browserController.delete()
|
||||||
|
taskManager.teardown()
|
||||||
|
|
||||||
|
|
||||||
# Initialize only controllers whose init functions
|
# 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
|
# 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)
|
# we cannot capture any local variables here (we must rely on globals)
|
||||||
var callback: SignalCallback = proc(p0: cstring) {.cdecl.} =
|
var callback: SignalCallback = proc(p0: cstring) {.cdecl.} =
|
||||||
setupForeignThreadGc()
|
|
||||||
signal_handler(signalsQObjPointer, p0, "receiveSignal")
|
signal_handler(signalsQObjPointer, p0, "receiveSignal")
|
||||||
tearDownForeignThreadGc()
|
|
||||||
|
|
||||||
status_go.setSignalEventCallback(callback)
|
status_go.setSignalEventCallback(callback)
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,12 @@ const domain* = ".stateofus.eth"
|
||||||
|
|
||||||
proc userName*(ensName: string, removeSuffix: bool = false): string =
|
proc userName*(ensName: string, removeSuffix: bool = false): string =
|
||||||
if ensName != "" and ensName.endsWith(domain):
|
if ensName != "" and ensName.endsWith(domain):
|
||||||
if removeSuffix:
|
if removeSuffix:
|
||||||
result = ensName.split(".")[0]
|
result = ensName.split(".")[0]
|
||||||
else:
|
else:
|
||||||
result = ensName
|
result = ensName
|
||||||
else:
|
else:
|
||||||
if ensName.endsWith(".eth") and removeSuffix:
|
if ensName.endsWith(".eth") and removeSuffix:
|
||||||
return ensName.split(".")[0]
|
return ensName.split(".")[0]
|
||||||
result = ensName
|
result = ensName
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ proc namehash*(ensName:string): string =
|
||||||
concatArrays[0..31] = node
|
concatArrays[0..31] = node
|
||||||
concatArrays[32..63] = elem
|
concatArrays[32..63] = elem
|
||||||
node = keccak_256.digest(concatArrays).data
|
node = keccak_256.digest(concatArrays).data
|
||||||
|
|
||||||
result = "0x" & node.toHex()
|
result = "0x" & node.toHex()
|
||||||
|
|
||||||
const registry* = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
const registry* = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
||||||
|
@ -81,7 +81,7 @@ proc resolver*(usernameHash: string): string =
|
||||||
result = "0x" & resolverAddr
|
result = "0x" & resolverAddr
|
||||||
|
|
||||||
const owner_signature = "0x02571be3" # owner(bytes32 node)
|
const owner_signature = "0x02571be3" # owner(bytes32 node)
|
||||||
proc owner*(username: string): string =
|
proc owner*(username: string): string =
|
||||||
var userNameHash = namehash(addDomain(username))
|
var userNameHash = namehash(addDomain(username))
|
||||||
userNameHash.removePrefix("0x")
|
userNameHash.removePrefix("0x")
|
||||||
let payload = %* [{
|
let payload = %* [{
|
||||||
|
@ -97,7 +97,7 @@ proc owner*(username: string): string =
|
||||||
result = "0x" & ownerAddr.substr(26)
|
result = "0x" & ownerAddr.substr(26)
|
||||||
|
|
||||||
const pubkey_signature = "0xc8690233" # pubkey(bytes32 node)
|
const pubkey_signature = "0xc8690233" # pubkey(bytes32 node)
|
||||||
proc pubkey*(username: string): string =
|
proc pubkey*(username: string): string =
|
||||||
var userNameHash = namehash(addDomain(username))
|
var userNameHash = namehash(addDomain(username))
|
||||||
userNameHash.removePrefix("0x")
|
userNameHash.removePrefix("0x")
|
||||||
let ensResolver = resolver(userNameHash)
|
let ensResolver = resolver(userNameHash)
|
||||||
|
@ -116,7 +116,7 @@ proc pubkey*(username: string): string =
|
||||||
result = "0x04" & pubkey
|
result = "0x04" & pubkey
|
||||||
|
|
||||||
const address_signature = "0x3b3b57de" # addr(bytes32 node)
|
const address_signature = "0x3b3b57de" # addr(bytes32 node)
|
||||||
proc address*(username: string): string =
|
proc address*(username: string): string =
|
||||||
var userNameHash = namehash(addDomain(username))
|
var userNameHash = namehash(addDomain(username))
|
||||||
userNameHash.removePrefix("0x")
|
userNameHash.removePrefix("0x")
|
||||||
let ensResolver = resolver(userNameHash)
|
let ensResolver = resolver(userNameHash)
|
||||||
|
@ -133,7 +133,7 @@ proc address*(username: string): string =
|
||||||
result = "0x" & address.substr(26)
|
result = "0x" & address.substr(26)
|
||||||
|
|
||||||
const contenthash_signature = "0xbc1c58d1" # contenthash(bytes32)
|
const contenthash_signature = "0xbc1c58d1" # contenthash(bytes32)
|
||||||
proc contenthash*(ensAddr: string): string =
|
proc contenthash*(ensAddr: string): string =
|
||||||
var ensHash = namehash(ensAddr)
|
var ensHash = namehash(ensAddr)
|
||||||
ensHash.removePrefix("0x")
|
ensHash.removePrefix("0x")
|
||||||
let ensResolver = resolver(ensHash)
|
let ensResolver = resolver(ensHash)
|
||||||
|
@ -147,7 +147,7 @@ proc contenthash*(ensAddr: string): string =
|
||||||
let bytesResponse = response.parseJson["result"].getStr;
|
let bytesResponse = response.parseJson["result"].getStr;
|
||||||
if bytesResponse == "0x":
|
if bytesResponse == "0x":
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
let size = fromHex(Stuint[256], bytesResponse[66..129]).truncate(int)
|
let size = fromHex(Stuint[256], bytesResponse[66..129]).truncate(int)
|
||||||
result = bytesResponse[130..129+size*2]
|
result = bytesResponse[130..129+size*2]
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ proc getPrice*(): Stuint[256] =
|
||||||
"to": $contract.address,
|
"to": $contract.address,
|
||||||
"data": contract.methods["getPrice"].encodeAbi()
|
"data": contract.methods["getPrice"].encodeAbi()
|
||||||
}, "latest"]
|
}, "latest"]
|
||||||
|
|
||||||
let responseStr = callPrivateRPC("eth_call", payload)
|
let responseStr = callPrivateRPC("eth_call", payload)
|
||||||
let response = Json.decode(responseStr, RpcResponse)
|
let response = Json.decode(responseStr, RpcResponse)
|
||||||
if not response.error.isNil:
|
if not response.error.isNil:
|
||||||
|
@ -180,7 +180,7 @@ proc registerUsernameEstimateGas*(username: string, address: string, pubKey: str
|
||||||
ensUsernamesContract = contracts.getContract("ens-usernames")
|
ensUsernamesContract = contracts.getContract("ens-usernames")
|
||||||
sntContract = contracts.getSntContract()
|
sntContract = contracts.getSntContract()
|
||||||
price = getPrice()
|
price = getPrice()
|
||||||
|
|
||||||
let
|
let
|
||||||
register = Register(label: label, account: parseAddress(address), x: x, y: y)
|
register = Register(label: label, account: parseAddress(address), x: x, y: y)
|
||||||
registerAbiEncoded = ensUsernamesContract.methods["register"].encodeAbi(register)
|
registerAbiEncoded = ensUsernamesContract.methods["register"].encodeAbi(register)
|
||||||
|
@ -188,7 +188,7 @@ proc registerUsernameEstimateGas*(username: string, address: string, pubKey: str
|
||||||
approveAndCallAbiEncoded = sntContract.methods["approveAndCall"].encodeAbi(approveAndCallObj)
|
approveAndCallAbiEncoded = sntContract.methods["approveAndCall"].encodeAbi(approveAndCallObj)
|
||||||
|
|
||||||
var tx = transactions.buildTokenTransaction(parseAddress(address), sntContract.address, "", "")
|
var tx = transactions.buildTokenTransaction(parseAddress(address), sntContract.address, "", "")
|
||||||
|
|
||||||
let response = sntContract.methods["approveAndCall"].estimateGas(tx, approveAndCallObj, success)
|
let response = sntContract.methods["approveAndCall"].estimateGas(tx, approveAndCallObj, success)
|
||||||
if success:
|
if success:
|
||||||
result = fromHex[int](response)
|
result = fromHex[int](response)
|
||||||
|
@ -202,11 +202,11 @@ proc registerUsername*(username, pubKey, address, gas, gasPrice, password: stri
|
||||||
ensUsernamesContract = contracts.getContract("ens-usernames")
|
ensUsernamesContract = contracts.getContract("ens-usernames")
|
||||||
sntContract = contracts.getSntContract()
|
sntContract = contracts.getSntContract()
|
||||||
price = getPrice()
|
price = getPrice()
|
||||||
|
|
||||||
let
|
let
|
||||||
register = Register(label: label, account: parseAddress(address), x: x, y: y)
|
register = Register(label: label, account: parseAddress(address), x: x, y: y)
|
||||||
registerAbiEncoded = ensUsernamesContract.methods["register"].encodeAbi(register)
|
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)
|
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)
|
resolverAddress = resolver(hash)
|
||||||
|
|
||||||
var tx = transactions.buildTokenTransaction(parseAddress(address), parseAddress(resolverAddress), "", "")
|
var tx = transactions.buildTokenTransaction(parseAddress(address), parseAddress(resolverAddress), "", "")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
let response = resolverContract.methods["setPubkey"].estimateGas(tx, setPubkey, success)
|
let response = resolverContract.methods["setPubkey"].estimateGas(tx, setPubkey, success)
|
||||||
if success:
|
if success:
|
||||||
|
@ -295,8 +295,10 @@ proc decodeENSContentHash*(value: string): tuple[ensType: ENSType, output: strin
|
||||||
# 12 = identifies sha2-256 hash
|
# 12 = identifies sha2-256 hash
|
||||||
# 20 = multihash length = 32
|
# 20 = multihash length = 32
|
||||||
# ...rest = multihash digest
|
# ...rest = multihash digest
|
||||||
let multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get()
|
let
|
||||||
return (ENSType.IPFS, $Cid.init(CIDv0, MultiCodec.codec(codec), multiHash))
|
multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get()
|
||||||
|
decoded = Cid.init(CIDv0, MultiCodec.codec(codec), multiHash).get()
|
||||||
|
return (ENSType.IPFS, $decoded)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Error decoding ENS contenthash", hash=value, exception=e.msg
|
error "Error decoding ENS contenthash", hash=value, exception=e.msg
|
||||||
raise
|
raise
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import
|
import
|
||||||
sequtils, sugar, macros, tables, strutils, locks
|
sequtils, sugar, macros, tables, strutils
|
||||||
|
|
||||||
import
|
import
|
||||||
web3/ethtypes, stew/byteutils, nimcrypto, json_serialization, chronicles
|
web3/ethtypes, stew/byteutils, nimcrypto, json_serialization, chronicles
|
||||||
|
@ -16,9 +16,6 @@ export
|
||||||
logScope:
|
logScope:
|
||||||
topics = "contracts"
|
topics = "contracts"
|
||||||
|
|
||||||
var contractsLock: Lock
|
|
||||||
initLock(contractsLock)
|
|
||||||
|
|
||||||
const ERC20_METHODS = @[
|
const ERC20_METHODS = @[
|
||||||
("name", Method(signature: "name()")),
|
("name", Method(signature: "name()")),
|
||||||
("symbol", Method(signature: "symbol()")),
|
("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)
|
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] = @[
|
var
|
||||||
# Mainnet contracts
|
contracts {.threadvar.}: seq[Contract]
|
||||||
newErc20Contract("Status Network Token", Network.Mainnet, parseAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"), "SNT", 18, true),
|
contractsInited {.threadvar.}: bool
|
||||||
newErc20Contract("Dai Stablecoin", Network.Mainnet, parseAddress("0x6b175474e89094c44da98b954eedeac495271d0f"), "DAI", 18, true),
|
proc allContracts(): seq[Contract] =
|
||||||
newErc20Contract("Sai Stablecoin v1.0", Network.Mainnet, parseAddress("0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"), "SAI", 18, true),
|
if contractsInited:
|
||||||
newErc20Contract("MKR", Network.Mainnet, parseAddress("0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2"), "MKR", 18, true),
|
result = contracts
|
||||||
newErc20Contract("EOS", Network.Mainnet, parseAddress("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"), "EOS", 18, true),
|
else:
|
||||||
newErc20Contract("OMGToken", Network.Mainnet, parseAddress("0xd26114cd6ee289accf82350c8d8487fedb8a0c07"), "OMG", 18, true),
|
contracts = @[
|
||||||
newErc20Contract("Populous Platform", Network.Mainnet, parseAddress("0xd4fa1460f537bb9085d22c7bccb5dd450ef28e3a"), "PPT", 8, true),
|
# Mainnet contracts
|
||||||
newErc20Contract("Reputation", Network.Mainnet, parseAddress("0x1985365e9f78359a9b6ad760e32412f4a445e862"), "REP", 18, true),
|
newErc20Contract("Status Network Token", Network.Mainnet, parseAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"), "SNT", 18, true),
|
||||||
newErc20Contract("PowerLedger", Network.Mainnet, parseAddress("0x595832f8fc6bf59c85c527fec3740a1b7a361269"), "POWR", 6, true),
|
newErc20Contract("Dai Stablecoin", Network.Mainnet, parseAddress("0x6b175474e89094c44da98b954eedeac495271d0f"), "DAI", 18, true),
|
||||||
newErc20Contract("TenX Pay Token", Network.Mainnet, parseAddress("0xb97048628db6b661d4c2aa833e95dbe1a905b280"), "PAY", 18, true),
|
newErc20Contract("Sai Stablecoin v1.0", Network.Mainnet, parseAddress("0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"), "SAI", 18, true),
|
||||||
newErc20Contract("Veros", Network.Mainnet, parseAddress("0x92e78dae1315067a8819efd6dca432de9dcde2e9"), "VRS", 6, false),
|
newErc20Contract("MKR", Network.Mainnet, parseAddress("0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2"), "MKR", 18, true),
|
||||||
newErc20Contract("Golem Network Token", Network.Mainnet, parseAddress("0xa74476443119a942de498590fe1f2454d7d4ac0d"), "GNT", 18, true),
|
newErc20Contract("EOS", Network.Mainnet, parseAddress("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"), "EOS", 18, true),
|
||||||
newErc20Contract("Salt", Network.Mainnet, parseAddress("0x4156d3342d5c385a87d264f90653733592000581"), "SALT", 8, true),
|
newErc20Contract("OMGToken", Network.Mainnet, parseAddress("0xd26114cd6ee289accf82350c8d8487fedb8a0c07"), "OMG", 18, true),
|
||||||
newErc20Contract("BNB", Network.Mainnet, parseAddress("0xb8c77482e45f1f44de1745f52c74426c631bdd52"), "BNB", 18, true),
|
newErc20Contract("Populous Platform", Network.Mainnet, parseAddress("0xd4fa1460f537bb9085d22c7bccb5dd450ef28e3a"), "PPT", 8, true),
|
||||||
newErc20Contract("Basic Attention Token", Network.Mainnet, parseAddress("0x0d8775f648430679a709e98d2b0cb6250d2887ef"), "BAT", 18, true),
|
newErc20Contract("Reputation", Network.Mainnet, parseAddress("0x1985365e9f78359a9b6ad760e32412f4a445e862"), "REP", 18, true),
|
||||||
newErc20Contract("Kyber Network Crystal", Network.Mainnet, parseAddress("0xdd974d5c2e2928dea5f71b9825b8b646686bd200"), "KNC", 18, true),
|
newErc20Contract("PowerLedger", Network.Mainnet, parseAddress("0x595832f8fc6bf59c85c527fec3740a1b7a361269"), "POWR", 6, true),
|
||||||
newErc20Contract("BTU Protocol", Network.Mainnet, parseAddress("0xb683D83a532e2Cb7DFa5275eED3698436371cc9f"), "BTU", 18, true),
|
newErc20Contract("TenX Pay Token", Network.Mainnet, parseAddress("0xb97048628db6b661d4c2aa833e95dbe1a905b280"), "PAY", 18, true),
|
||||||
newErc20Contract("Digix DAO", Network.Mainnet, parseAddress("0xe0b7927c4af23765cb51314a0e0521a9645f0e2a"), "DGD", 9, true),
|
newErc20Contract("Veros", Network.Mainnet, parseAddress("0x92e78dae1315067a8819efd6dca432de9dcde2e9"), "VRS", 6, false),
|
||||||
newErc20Contract("Aeternity", Network.Mainnet, parseAddress("0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d"), "AE", 18, true),
|
newErc20Contract("Golem Network Token", Network.Mainnet, parseAddress("0xa74476443119a942de498590fe1f2454d7d4ac0d"), "GNT", 18, true),
|
||||||
newErc20Contract("Tronix", Network.Mainnet, parseAddress("0xf230b790e05390fc8295f4d3f60332c93bed42e2"), "TRX", 6, true),
|
newErc20Contract("Salt", Network.Mainnet, parseAddress("0x4156d3342d5c385a87d264f90653733592000581"), "SALT", 8, true),
|
||||||
newErc20Contract("Ethos", Network.Mainnet, parseAddress("0x5af2be193a6abca9c8817001f45744777db30756"), "ETHOS", 8, true),
|
newErc20Contract("BNB", Network.Mainnet, parseAddress("0xb8c77482e45f1f44de1745f52c74426c631bdd52"), "BNB", 18, true),
|
||||||
newErc20Contract("Raiden Token", Network.Mainnet, parseAddress("0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6"), "RDN", 18, true),
|
newErc20Contract("Basic Attention Token", Network.Mainnet, parseAddress("0x0d8775f648430679a709e98d2b0cb6250d2887ef"), "BAT", 18, true),
|
||||||
newErc20Contract("SingularDTV", Network.Mainnet, parseAddress("0xaec2e87e0a235266d9c5adc9deb4b2e29b54d009"), "SNGLS", 0, true),
|
newErc20Contract("Kyber Network Crystal", Network.Mainnet, parseAddress("0xdd974d5c2e2928dea5f71b9825b8b646686bd200"), "KNC", 18, true),
|
||||||
newErc20Contract("Gnosis Token", Network.Mainnet, parseAddress("0x6810e776880c02933d47db1b9fc05908e5386b96"), "GNO", 18, true),
|
newErc20Contract("BTU Protocol", Network.Mainnet, parseAddress("0xb683D83a532e2Cb7DFa5275eED3698436371cc9f"), "BTU", 18, true),
|
||||||
newErc20Contract("StorjToken", Network.Mainnet, parseAddress("0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac"), "STORJ", 8, true),
|
newErc20Contract("Digix DAO", Network.Mainnet, parseAddress("0xe0b7927c4af23765cb51314a0e0521a9645f0e2a"), "DGD", 9, true),
|
||||||
newErc20Contract("AdEx", Network.Mainnet, parseAddress("0x4470bb87d77b963a013db939be332f927f2b992e"), "ADX", 4, false),
|
newErc20Contract("Aeternity", Network.Mainnet, parseAddress("0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d"), "AE", 18, true),
|
||||||
newErc20Contract("FunFair", Network.Mainnet, parseAddress("0x419d0d8bdd9af5e606ae2232ed285aff190e711b"), "FUN", 8, true),
|
newErc20Contract("Tronix", Network.Mainnet, parseAddress("0xf230b790e05390fc8295f4d3f60332c93bed42e2"), "TRX", 6, true),
|
||||||
newErc20Contract("Civic", Network.Mainnet, parseAddress("0x41e5560054824ea6b0732e656e3ad64e20e94e45"), "CVC", 8, true),
|
newErc20Contract("Ethos", Network.Mainnet, parseAddress("0x5af2be193a6abca9c8817001f45744777db30756"), "ETHOS", 8, true),
|
||||||
newErc20Contract("ICONOMI", Network.Mainnet, parseAddress("0x888666ca69e0f178ded6d75b5726cee99a87d698"), "ICN", 18, true),
|
newErc20Contract("Raiden Token", Network.Mainnet, parseAddress("0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6"), "RDN", 18, true),
|
||||||
newErc20Contract("Walton Token", Network.Mainnet, parseAddress("0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74"), "WTC", 18, true),
|
newErc20Contract("SingularDTV", Network.Mainnet, parseAddress("0xaec2e87e0a235266d9c5adc9deb4b2e29b54d009"), "SNGLS", 0, true),
|
||||||
newErc20Contract("Bytom", Network.Mainnet, parseAddress("0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750"), "BTM", 8, true),
|
newErc20Contract("Gnosis Token", Network.Mainnet, parseAddress("0x6810e776880c02933d47db1b9fc05908e5386b96"), "GNO", 18, true),
|
||||||
newErc20Contract("0x Protocol Token", Network.Mainnet, parseAddress("0xe41d2489571d322189246dafa5ebde1f4699f498"), "ZRX", 18, true),
|
newErc20Contract("StorjToken", Network.Mainnet, parseAddress("0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac"), "STORJ", 8, true),
|
||||||
newErc20Contract("Bancor Network Token", Network.Mainnet, parseAddress("0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c"), "BNT", 18, true),
|
newErc20Contract("AdEx", Network.Mainnet, parseAddress("0x4470bb87d77b963a013db939be332f927f2b992e"), "ADX", 4, false),
|
||||||
newErc20Contract("Metal", Network.Mainnet, parseAddress("0xf433089366899d83a9f26a773d59ec7ecf30355e"), "MTL", 8, false),
|
newErc20Contract("FunFair", Network.Mainnet, parseAddress("0x419d0d8bdd9af5e606ae2232ed285aff190e711b"), "FUN", 8, true),
|
||||||
newErc20Contract("PayPie", Network.Mainnet, parseAddress("0xc42209accc14029c1012fb5680d95fbd6036e2a0"), "PPP", 18, true),
|
newErc20Contract("Civic", Network.Mainnet, parseAddress("0x41e5560054824ea6b0732e656e3ad64e20e94e45"), "CVC", 8, true),
|
||||||
newErc20Contract("ChainLink Token", Network.Mainnet, parseAddress("0x514910771af9ca656af840dff83e8264ecf986ca"), "LINK", 18, true),
|
newErc20Contract("ICONOMI", Network.Mainnet, parseAddress("0x888666ca69e0f178ded6d75b5726cee99a87d698"), "ICN", 18, true),
|
||||||
newErc20Contract("Kin", Network.Mainnet, parseAddress("0x818fc6c2ec5986bc6e2cbf00939d90556ab12ce5"), "KIN", 18, true),
|
newErc20Contract("Walton Token", Network.Mainnet, parseAddress("0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74"), "WTC", 18, true),
|
||||||
newErc20Contract("Aragon Network Token", Network.Mainnet, parseAddress("0x960b236a07cf122663c4303350609a66a7b288c0"), "ANT", 18, true),
|
newErc20Contract("Bytom", Network.Mainnet, parseAddress("0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750"), "BTM", 8, true),
|
||||||
newErc20Contract("MobileGo Token", Network.Mainnet, parseAddress("0x40395044ac3c0c57051906da938b54bd6557f212"), "MGO", 8, true),
|
newErc20Contract("0x Protocol Token", Network.Mainnet, parseAddress("0xe41d2489571d322189246dafa5ebde1f4699f498"), "ZRX", 18, true),
|
||||||
newErc20Contract("Monaco", Network.Mainnet, parseAddress("0xb63b606ac810a52cca15e44bb630fd42d8d1d83d"), "MCO", 8, true),
|
newErc20Contract("Bancor Network Token", Network.Mainnet, parseAddress("0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c"), "BNT", 18, true),
|
||||||
newErc20Contract("loopring", Network.Mainnet, parseAddress("0xef68e7c694f40c8202821edf525de3782458639f"), "LRC", 18, true),
|
newErc20Contract("Metal", Network.Mainnet, parseAddress("0xf433089366899d83a9f26a773d59ec7ecf30355e"), "MTL", 8, false),
|
||||||
newErc20Contract("Zeus Shield Coin", Network.Mainnet, parseAddress("0x7a41e0517a5eca4fdbc7fbeba4d4c47b9ff6dc63"), "ZSC", 18, true),
|
newErc20Contract("PayPie", Network.Mainnet, parseAddress("0xc42209accc14029c1012fb5680d95fbd6036e2a0"), "PPP", 18, true),
|
||||||
newErc20Contract("Streamr DATAcoin", Network.Mainnet, parseAddress("0x0cf0ee63788a0849fe5297f3407f701e122cc023"), "DATA", 18, true),
|
newErc20Contract("ChainLink Token", Network.Mainnet, parseAddress("0x514910771af9ca656af840dff83e8264ecf986ca"), "LINK", 18, true),
|
||||||
newErc20Contract("Ripio Credit Network Token", Network.Mainnet, parseAddress("0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6"), "RCN", 18, true),
|
newErc20Contract("Kin", Network.Mainnet, parseAddress("0x818fc6c2ec5986bc6e2cbf00939d90556ab12ce5"), "KIN", 18, true),
|
||||||
newErc20Contract("WINGS", Network.Mainnet, parseAddress("0x667088b212ce3d06a1b553a7221e1fd19000d9af"), "WINGS", 18, true),
|
newErc20Contract("Aragon Network Token", Network.Mainnet, parseAddress("0x960b236a07cf122663c4303350609a66a7b288c0"), "ANT", 18, true),
|
||||||
newErc20Contract("Edgeless", Network.Mainnet, parseAddress("0x08711d3b02c8758f2fb3ab4e80228418a7f8e39c"), "EDG", 0, true),
|
newErc20Contract("MobileGo Token", Network.Mainnet, parseAddress("0x40395044ac3c0c57051906da938b54bd6557f212"), "MGO", 8, true),
|
||||||
newErc20Contract("Melon Token", Network.Mainnet, parseAddress("0xbeb9ef514a379b997e0798fdcc901ee474b6d9a1"), "MLN", 18, true),
|
newErc20Contract("Monaco", Network.Mainnet, parseAddress("0xb63b606ac810a52cca15e44bb630fd42d8d1d83d"), "MCO", 8, true),
|
||||||
newErc20Contract("Moeda Loyalty Points", Network.Mainnet, parseAddress("0x51db5ad35c671a87207d88fc11d593ac0c8415bd"), "MDA", 18, true),
|
newErc20Contract("loopring", Network.Mainnet, parseAddress("0xef68e7c694f40c8202821edf525de3782458639f"), "LRC", 18, true),
|
||||||
newErc20Contract("PILLAR", Network.Mainnet, parseAddress("0xe3818504c1b32bf1557b16c238b2e01fd3149c17"), "PLR", 18, true),
|
newErc20Contract("Zeus Shield Coin", Network.Mainnet, parseAddress("0x7a41e0517a5eca4fdbc7fbeba4d4c47b9ff6dc63"), "ZSC", 18, true),
|
||||||
newErc20Contract("QRL", Network.Mainnet, parseAddress("0x697beac28b09e122c4332d163985e8a73121b97f"), "QRL", 8, true),
|
newErc20Contract("Streamr DATAcoin", Network.Mainnet, parseAddress("0x0cf0ee63788a0849fe5297f3407f701e122cc023"), "DATA", 18, true),
|
||||||
newErc20Contract("Modum Token", Network.Mainnet, parseAddress("0x957c30ab0426e0c93cd8241e2c60392d08c6ac8e"), "MOD", 0, true),
|
newErc20Contract("Ripio Credit Network Token", Network.Mainnet, parseAddress("0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6"), "RCN", 18, true),
|
||||||
newErc20Contract("Token-as-a-Service", Network.Mainnet, parseAddress("0xe7775a6e9bcf904eb39da2b68c5efb4f9360e08c"), "TAAS", 6, true),
|
newErc20Contract("WINGS", Network.Mainnet, parseAddress("0x667088b212ce3d06a1b553a7221e1fd19000d9af"), "WINGS", 18, true),
|
||||||
newErc20Contract("GRID Token", Network.Mainnet, parseAddress("0x12b19d3e2ccc14da04fae33e63652ce469b3f2fd"), "GRID", 12, true),
|
newErc20Contract("Edgeless", Network.Mainnet, parseAddress("0x08711d3b02c8758f2fb3ab4e80228418a7f8e39c"), "EDG", 0, true),
|
||||||
newErc20Contract("SANtiment network token", Network.Mainnet, parseAddress("0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098"), "SAN", 18, true),
|
newErc20Contract("Melon Token", Network.Mainnet, parseAddress("0xbeb9ef514a379b997e0798fdcc901ee474b6d9a1"), "MLN", 18, true),
|
||||||
newErc20Contract("SONM Token", Network.Mainnet, parseAddress("0x983f6d60db79ea8ca4eb9968c6aff8cfa04b3c63"), "SNM", 18, true),
|
newErc20Contract("Moeda Loyalty Points", Network.Mainnet, parseAddress("0x51db5ad35c671a87207d88fc11d593ac0c8415bd"), "MDA", 18, true),
|
||||||
newErc20Contract("Request Token", Network.Mainnet, parseAddress("0x8f8221afbb33998d8584a2b05749ba73c37a938a"), "REQ", 18, true),
|
newErc20Contract("PILLAR", Network.Mainnet, parseAddress("0xe3818504c1b32bf1557b16c238b2e01fd3149c17"), "PLR", 18, true),
|
||||||
newErc20Contract("Substratum", Network.Mainnet, parseAddress("0x12480e24eb5bec1a9d4369cab6a80cad3c0a377a"), "SUB", 2, true),
|
newErc20Contract("QRL", Network.Mainnet, parseAddress("0x697beac28b09e122c4332d163985e8a73121b97f"), "QRL", 8, true),
|
||||||
newErc20Contract("Decentraland MANA", Network.Mainnet, parseAddress("0x0f5d2fb29fb7d3cfee444a200298f468908cc942"), "MANA", 18, true),
|
newErc20Contract("Modum Token", Network.Mainnet, parseAddress("0x957c30ab0426e0c93cd8241e2c60392d08c6ac8e"), "MOD", 0, true),
|
||||||
newErc20Contract("AirSwap Token", Network.Mainnet, parseAddress("0x27054b13b1b798b345b591a4d22e6562d47ea75a"), "AST", 4, true),
|
newErc20Contract("Token-as-a-Service", Network.Mainnet, parseAddress("0xe7775a6e9bcf904eb39da2b68c5efb4f9360e08c"), "TAAS", 6, true),
|
||||||
newErc20Contract("R token", Network.Mainnet, parseAddress("0x48f775efbe4f5ece6e0df2f7b5932df56823b990"), "R", 0, true),
|
newErc20Contract("GRID Token", Network.Mainnet, parseAddress("0x12b19d3e2ccc14da04fae33e63652ce469b3f2fd"), "GRID", 12, true),
|
||||||
newErc20Contract("FirstBlood Token", Network.Mainnet, parseAddress("0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7"), "1ST", 18, true),
|
newErc20Contract("SANtiment network token", Network.Mainnet, parseAddress("0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098"), "SAN", 18, true),
|
||||||
newErc20Contract("Cofoundit", Network.Mainnet, parseAddress("0x12fef5e57bf45873cd9b62e9dbd7bfb99e32d73e"), "CFI", 18, true),
|
newErc20Contract("SONM Token", Network.Mainnet, parseAddress("0x983f6d60db79ea8ca4eb9968c6aff8cfa04b3c63"), "SNM", 18, true),
|
||||||
newErc20Contract("Enigma", Network.Mainnet, parseAddress("0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4"), "ENG", 8, true),
|
newErc20Contract("Request Token", Network.Mainnet, parseAddress("0x8f8221afbb33998d8584a2b05749ba73c37a938a"), "REQ", 18, true),
|
||||||
newErc20Contract("Amber Token", Network.Mainnet, parseAddress("0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce"), "AMB", 18, true),
|
newErc20Contract("Substratum", Network.Mainnet, parseAddress("0x12480e24eb5bec1a9d4369cab6a80cad3c0a377a"), "SUB", 2, true),
|
||||||
newErc20Contract("XPlay Token", Network.Mainnet, parseAddress("0x90528aeb3a2b736b780fd1b6c478bb7e1d643170"), "XPA", 18, true),
|
newErc20Contract("Decentraland MANA", Network.Mainnet, parseAddress("0x0f5d2fb29fb7d3cfee444a200298f468908cc942"), "MANA", 18, true),
|
||||||
newErc20Contract("Open Trading Network", Network.Mainnet, parseAddress("0x881ef48211982d01e2cb7092c915e647cd40d85c"), "OTN", 18, true),
|
newErc20Contract("AirSwap Token", Network.Mainnet, parseAddress("0x27054b13b1b798b345b591a4d22e6562d47ea75a"), "AST", 4, true),
|
||||||
newErc20Contract("Trustcoin", Network.Mainnet, parseAddress("0xcb94be6f13a1182e4a4b6140cb7bf2025d28e41b"), "TRST", 6, true),
|
newErc20Contract("R token", Network.Mainnet, parseAddress("0x48f775efbe4f5ece6e0df2f7b5932df56823b990"), "R", 0, true),
|
||||||
newErc20Contract("Monolith TKN", Network.Mainnet, parseAddress("0xaaaf91d9b90df800df4f55c205fd6989c977e73a"), "TKN", 8, true),
|
newErc20Contract("FirstBlood Token", Network.Mainnet, parseAddress("0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7"), "1ST", 18, true),
|
||||||
newErc20Contract("RHOC", Network.Mainnet, parseAddress("0x168296bb09e24a88805cb9c33356536b980d3fc5"), "RHOC", 8, true),
|
newErc20Contract("Cofoundit", Network.Mainnet, parseAddress("0x12fef5e57bf45873cd9b62e9dbd7bfb99e32d73e"), "CFI", 18, true),
|
||||||
newErc20Contract("Target Coin", Network.Mainnet, parseAddress("0xac3da587eac229c9896d919abc235ca4fd7f72c1"), "TGT", 1, false),
|
newErc20Contract("Enigma", Network.Mainnet, parseAddress("0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4"), "ENG", 8, true),
|
||||||
newErc20Contract("Everex", Network.Mainnet, parseAddress("0xf3db5fa2c66b7af3eb0c0b782510816cbe4813b8"), "EVX", 4, true),
|
newErc20Contract("Amber Token", Network.Mainnet, parseAddress("0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce"), "AMB", 18, true),
|
||||||
newErc20Contract("ICOS", Network.Mainnet, parseAddress("0x014b50466590340d41307cc54dcee990c8d58aa8"), "ICOS", 6, true),
|
newErc20Contract("XPlay Token", Network.Mainnet, parseAddress("0x90528aeb3a2b736b780fd1b6c478bb7e1d643170"), "XPA", 18, true),
|
||||||
newErc20Contract("district0x Network Token", Network.Mainnet, parseAddress("0x0abdace70d3790235af448c88547603b945604ea"), "DNT", 18, true),
|
newErc20Contract("Open Trading Network", Network.Mainnet, parseAddress("0x881ef48211982d01e2cb7092c915e647cd40d85c"), "OTN", 18, true),
|
||||||
newErc20Contract("Dentacoin", Network.Mainnet, parseAddress("0x08d32b0da63e2c3bcf8019c9c5d849d7a9d791e6"), "٨", 0, false),
|
newErc20Contract("Trustcoin", Network.Mainnet, parseAddress("0xcb94be6f13a1182e4a4b6140cb7bf2025d28e41b"), "TRST", 6, true),
|
||||||
newErc20Contract("Eidoo Token", Network.Mainnet, parseAddress("0xced4e93198734ddaff8492d525bd258d49eb388e"), "EDO", 18, true),
|
newErc20Contract("Monolith TKN", Network.Mainnet, parseAddress("0xaaaf91d9b90df800df4f55c205fd6989c977e73a"), "TKN", 8, true),
|
||||||
newErc20Contract("BitDice", Network.Mainnet, parseAddress("0x29d75277ac7f0335b2165d0895e8725cbf658d73"), "CSNO", 8, false),
|
newErc20Contract("RHOC", Network.Mainnet, parseAddress("0x168296bb09e24a88805cb9c33356536b980d3fc5"), "RHOC", 8, true),
|
||||||
newErc20Contract("Cobinhood Token", Network.Mainnet, parseAddress("0xb2f7eb1f2c37645be61d73953035360e768d81e6"), "COB", 18, true),
|
newErc20Contract("Target Coin", Network.Mainnet, parseAddress("0xac3da587eac229c9896d919abc235ca4fd7f72c1"), "TGT", 1, false),
|
||||||
newErc20Contract("Enjin Coin", Network.Mainnet, parseAddress("0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c"), "ENJ", 18, false),
|
newErc20Contract("Everex", Network.Mainnet, parseAddress("0xf3db5fa2c66b7af3eb0c0b782510816cbe4813b8"), "EVX", 4, true),
|
||||||
newErc20Contract("AVENTUS", Network.Mainnet, parseAddress("0x0d88ed6e74bbfd96b831231638b66c05571e824f"), "AVT", 18, false),
|
newErc20Contract("ICOS", Network.Mainnet, parseAddress("0x014b50466590340d41307cc54dcee990c8d58aa8"), "ICOS", 6, true),
|
||||||
newErc20Contract("Chronobank TIME", Network.Mainnet, parseAddress("0x6531f133e6deebe7f2dce5a0441aa7ef330b4e53"), "TIME", 8, false),
|
newErc20Contract("district0x Network Token", Network.Mainnet, parseAddress("0x0abdace70d3790235af448c88547603b945604ea"), "DNT", 18, true),
|
||||||
newErc20Contract("Cindicator Token", Network.Mainnet, parseAddress("0xd4c435f5b09f855c3317c8524cb1f586e42795fa"), "CND", 18, true),
|
newErc20Contract("Dentacoin", Network.Mainnet, parseAddress("0x08d32b0da63e2c3bcf8019c9c5d849d7a9d791e6"), "٨", 0, false),
|
||||||
newErc20Contract("Stox", Network.Mainnet, parseAddress("0x006bea43baa3f7a6f765f14f10a1a1b08334ef45"), "STX", 18, true),
|
newErc20Contract("Eidoo Token", Network.Mainnet, parseAddress("0xced4e93198734ddaff8492d525bd258d49eb388e"), "EDO", 18, true),
|
||||||
newErc20Contract("Xaurum", Network.Mainnet, parseAddress("0x4df812f6064def1e5e029f1ca858777cc98d2d81"), "XAUR", 8, true),
|
newErc20Contract("BitDice", Network.Mainnet, parseAddress("0x29d75277ac7f0335b2165d0895e8725cbf658d73"), "CSNO", 8, false),
|
||||||
newErc20Contract("Vibe", Network.Mainnet, parseAddress("0x2c974b2d0ba1716e644c1fc59982a89ddd2ff724"), "VIB", 18, true),
|
newErc20Contract("Cobinhood Token", Network.Mainnet, parseAddress("0xb2f7eb1f2c37645be61d73953035360e768d81e6"), "COB", 18, true),
|
||||||
newErc20Contract("PRG", Network.Mainnet, parseAddress("0x7728dfef5abd468669eb7f9b48a7f70a501ed29d"), "PRG", 6, false),
|
newErc20Contract("Enjin Coin", Network.Mainnet, parseAddress("0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c"), "ENJ", 18, false),
|
||||||
newErc20Contract("Delphy Token", Network.Mainnet, parseAddress("0x6c2adc2073994fb2ccc5032cc2906fa221e9b391"), "DPY", 18, true),
|
newErc20Contract("AVENTUS", Network.Mainnet, parseAddress("0x0d88ed6e74bbfd96b831231638b66c05571e824f"), "AVT", 18, false),
|
||||||
newErc20Contract("CoinDash Token", Network.Mainnet, parseAddress("0x2fe6ab85ebbf7776fee46d191ee4cea322cecf51"), "CDT", 18, true),
|
newErc20Contract("Chronobank TIME", Network.Mainnet, parseAddress("0x6531f133e6deebe7f2dce5a0441aa7ef330b4e53"), "TIME", 8, false),
|
||||||
newErc20Contract("Tierion Network Token", Network.Mainnet, parseAddress("0x08f5a9235b08173b7569f83645d2c7fb55e8ccd8"), "TNT", 8, true),
|
newErc20Contract("Cindicator Token", Network.Mainnet, parseAddress("0xd4c435f5b09f855c3317c8524cb1f586e42795fa"), "CND", 18, true),
|
||||||
newErc20Contract("DomRaiderToken", Network.Mainnet, parseAddress("0x9af4f26941677c706cfecf6d3379ff01bb85d5ab"), "DRT", 8, true),
|
newErc20Contract("Stox", Network.Mainnet, parseAddress("0x006bea43baa3f7a6f765f14f10a1a1b08334ef45"), "STX", 18, true),
|
||||||
newErc20Contract("SPANK", Network.Mainnet, parseAddress("0x42d6622dece394b54999fbd73d108123806f6a18"), "SPANK", 18, true),
|
newErc20Contract("Xaurum", Network.Mainnet, parseAddress("0x4df812f6064def1e5e029f1ca858777cc98d2d81"), "XAUR", 8, true),
|
||||||
newErc20Contract("Berlin Coin", Network.Mainnet, parseAddress("0x80046305aaab08f6033b56a360c184391165dc2d"), "BRLN", 18, true),
|
newErc20Contract("Vibe", Network.Mainnet, parseAddress("0x2c974b2d0ba1716e644c1fc59982a89ddd2ff724"), "VIB", 18, true),
|
||||||
newErc20Contract("USD//C", Network.Mainnet, parseAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), "USDC", 6, true),
|
newErc20Contract("PRG", Network.Mainnet, parseAddress("0x7728dfef5abd468669eb7f9b48a7f70a501ed29d"), "PRG", 6, false),
|
||||||
newErc20Contract("Livepeer Token", Network.Mainnet, parseAddress("0x58b6a8a3302369daec383334672404ee733ab239"), "LPT", 18, true),
|
newErc20Contract("Delphy Token", Network.Mainnet, parseAddress("0x6c2adc2073994fb2ccc5032cc2906fa221e9b391"), "DPY", 18, true),
|
||||||
newErc20Contract("Simple Token", Network.Mainnet, parseAddress("0x2c4e8f2d746113d0696ce89b35f0d8bf88e0aeca"), "ST", 18, true),
|
newErc20Contract("CoinDash Token", Network.Mainnet, parseAddress("0x2fe6ab85ebbf7776fee46d191ee4cea322cecf51"), "CDT", 18, true),
|
||||||
newErc20Contract("Wrapped BTC", Network.Mainnet, parseAddress("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"), "WBTC", 8, true),
|
newErc20Contract("Tierion Network Token", Network.Mainnet, parseAddress("0x08f5a9235b08173b7569f83645d2c7fb55e8ccd8"), "TNT", 8, true),
|
||||||
newErc20Contract("Bloom Token", Network.Mainnet, parseAddress("0x107c4504cd79c5d2696ea0030a8dd4e92601b82e"), "BLT", 18, true),
|
newErc20Contract("DomRaiderToken", Network.Mainnet, parseAddress("0x9af4f26941677c706cfecf6d3379ff01bb85d5ab"), "DRT", 8, true),
|
||||||
Contract(name: "stickers", network: Network.Mainnet, address: parseAddress("0x0577215622f43a39f4bc9640806dfea9b10d2a36"),
|
newErc20Contract("SPANK", Network.Mainnet, parseAddress("0x42d6622dece394b54999fbd73d108123806f6a18"), "SPANK", 18, true),
|
||||||
methods: [
|
newErc20Contract("Berlin Coin", Network.Mainnet, parseAddress("0x80046305aaab08f6033b56a360c184391165dc2d"), "BRLN", 18, true),
|
||||||
("packCount", Method(signature: "packCount()")),
|
newErc20Contract("USD//C", Network.Mainnet, parseAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), "USDC", 6, true),
|
||||||
("getPackData", Method(signature: "getPackData(uint256)"))
|
newErc20Contract("Livepeer Token", Network.Mainnet, parseAddress("0x58b6a8a3302369daec383334672404ee733ab239"), "LPT", 18, true),
|
||||||
].toTable
|
newErc20Contract("Simple Token", Network.Mainnet, parseAddress("0x2c4e8f2d746113d0696ce89b35f0d8bf88e0aeca"), "ST", 18, true),
|
||||||
),
|
newErc20Contract("Wrapped BTC", Network.Mainnet, parseAddress("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"), "WBTC", 8, true),
|
||||||
Contract(name: "sticker-market", network: Network.Mainnet, address: parseAddress("0x12824271339304d3a9f7e096e62a2a7e73b4a7e7"),
|
newErc20Contract("Bloom Token", Network.Mainnet, parseAddress("0x107c4504cd79c5d2696ea0030a8dd4e92601b82e"), "BLT", 18, true),
|
||||||
methods: [
|
Contract(name: "stickers", network: Network.Mainnet, address: parseAddress("0x0577215622f43a39f4bc9640806dfea9b10d2a36"),
|
||||||
("buyToken", Method(signature: "buyToken(uint256,address,uint256)"))
|
methods: [
|
||||||
].toTable
|
("packCount", Method(signature: "packCount()")),
|
||||||
),
|
("getPackData", Method(signature: "getPackData(uint256)"))
|
||||||
newErc721Contract("sticker-pack", Network.Mainnet, parseAddress("0x110101156e8F0743948B2A61aFcf3994A8Fb172e"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]),
|
].toTable
|
||||||
# Strikers seems dead. Their website doesn't work anymore
|
),
|
||||||
newErc721Contract("strikers", Network.Mainnet, parseAddress("0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"), "STRK", true),
|
Contract(name: "sticker-market", network: Network.Mainnet, address: parseAddress("0x12824271339304d3a9f7e096e62a2a7e73b4a7e7"),
|
||||||
newErc721Contract("ethermon", Network.Mainnet, parseAddress("0xb2c0782ae4a299f7358758b2d15da9bf29e1dd99"), "EMONA", true),
|
methods: [
|
||||||
newErc721Contract("kudos", Network.Mainnet, parseAddress("0x2aea4add166ebf38b63d09a75de1a7b94aa24163"), "KDO", true),
|
("buyToken", Method(signature: "buyToken(uint256,address,uint256)"))
|
||||||
newErc721Contract("crypto-kitties", Network.Mainnet, parseAddress("0x06012c8cf97bead5deae237070f9587f8e7a266d"), "CK", true),
|
].toTable
|
||||||
Contract(name: "ens-usernames", network: Network.Mainnet, address: parseAddress("0xDB5ac1a559b02E12F29fC0eC0e37Be8E046DEF49"),
|
),
|
||||||
methods: [
|
newErc721Contract("sticker-pack", Network.Mainnet, parseAddress("0x110101156e8F0743948B2A61aFcf3994A8Fb172e"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]),
|
||||||
("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")),
|
# Strikers seems dead. Their website doesn't work anymore
|
||||||
("getPrice", Method(signature: "getPrice()"))
|
newErc721Contract("strikers", Network.Mainnet, parseAddress("0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"), "STRK", true),
|
||||||
].toTable
|
newErc721Contract("ethermon", Network.Mainnet, parseAddress("0xb2c0782ae4a299f7358758b2d15da9bf29e1dd99"), "EMONA", true),
|
||||||
),
|
newErc721Contract("kudos", Network.Mainnet, parseAddress("0x2aea4add166ebf38b63d09a75de1a7b94aa24163"), "KDO", true),
|
||||||
Contract(name: "ens-resolver", network: Network.Mainnet, address: parseAddress("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41"),
|
newErc721Contract("crypto-kitties", Network.Mainnet, parseAddress("0x06012c8cf97bead5deae237070f9587f8e7a266d"), "CK", true),
|
||||||
methods: [
|
Contract(name: "ens-usernames", network: Network.Mainnet, address: parseAddress("0xDB5ac1a559b02E12F29fC0eC0e37Be8E046DEF49"),
|
||||||
("setPubkey", Method(signature: "setPubkey(bytes32,bytes32,bytes32)"))
|
methods: [
|
||||||
].toTable
|
("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
|
# Testnet (Ropsten) contracts
|
||||||
newErc20Contract("Status Test Token", Network.Testnet, parseAddress("0xc55cf4b03948d7ebc8b9e8bad92643703811d162"), "STT", 18, true),
|
newErc20Contract("Status Test Token", Network.Testnet, parseAddress("0xc55cf4b03948d7ebc8b9e8bad92643703811d162"), "STT", 18, true),
|
||||||
newErc20Contract("Handy Test Token", Network.Testnet, parseAddress("0xdee43a267e8726efd60c2e7d5b81552dcd4fa35c"), "HND", 0, false),
|
newErc20Contract("Handy Test Token", Network.Testnet, parseAddress("0xdee43a267e8726efd60c2e7d5b81552dcd4fa35c"), "HND", 0, false),
|
||||||
newErc20Contract("Lucky Test Token", Network.Testnet, parseAddress("0x703d7dc0bc8e314d65436adf985dda51e09ad43b"), "LXS", 2, false),
|
newErc20Contract("Lucky Test Token", Network.Testnet, parseAddress("0x703d7dc0bc8e314d65436adf985dda51e09ad43b"), "LXS", 2, false),
|
||||||
newErc20Contract("Adi Test Token", Network.Testnet, parseAddress("0xe639e24346d646e927f323558e6e0031bfc93581"), "ADI", 7, false),
|
newErc20Contract("Adi Test Token", Network.Testnet, parseAddress("0xe639e24346d646e927f323558e6e0031bfc93581"), "ADI", 7, false),
|
||||||
newErc20Contract("Wagner Test Token", Network.Testnet, parseAddress("0x2e7cd05f437eb256f363417fd8f920e2efa77540"), "WGN", 10, false),
|
newErc20Contract("Wagner Test Token", Network.Testnet, parseAddress("0x2e7cd05f437eb256f363417fd8f920e2efa77540"), "WGN", 10, false),
|
||||||
newErc20Contract("Modest Test Token", Network.Testnet, parseAddress("0x57cc9b83730e6d22b224e9dc3e370967b44a2de0"), "MDS", 18, 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: "tribute-to-talk", network: Network.Testnet, address: parseAddress("0xC61aa0287247a0398589a66fCD6146EC0F295432")),
|
||||||
Contract(name: "stickers", network: Network.Testnet, address: parseAddress("0x8cc272396be7583c65bee82cd7b743c69a87287d"),
|
Contract(name: "stickers", network: Network.Testnet, address: parseAddress("0x8cc272396be7583c65bee82cd7b743c69a87287d"),
|
||||||
methods: [
|
methods: [
|
||||||
("packCount", Method(signature: "packCount()")),
|
("packCount", Method(signature: "packCount()")),
|
||||||
("getPackData", Method(signature: "getPackData(uint256)"))
|
("getPackData", Method(signature: "getPackData(uint256)"))
|
||||||
].toTable
|
].toTable
|
||||||
),
|
),
|
||||||
Contract(name: "sticker-market", network: Network.Testnet, address: parseAddress("0x6CC7274aF9cE9572d22DFD8545Fb8c9C9Bcb48AD"),
|
Contract(name: "sticker-market", network: Network.Testnet, address: parseAddress("0x6CC7274aF9cE9572d22DFD8545Fb8c9C9Bcb48AD"),
|
||||||
methods: [
|
methods: [
|
||||||
("buyToken", Method(signature: "buyToken(uint256,address,uint256)"))
|
("buyToken", Method(signature: "buyToken(uint256,address,uint256)"))
|
||||||
].toTable
|
].toTable
|
||||||
),
|
),
|
||||||
newErc721Contract("sticker-pack", Network.Testnet, parseAddress("0xf852198d0385c4b871e0b91804ecd47c6ba97351"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]),
|
newErc721Contract("sticker-pack", Network.Testnet, parseAddress("0xf852198d0385c4b871e0b91804ecd47c6ba97351"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]),
|
||||||
newErc721Contract("kudos", Network.Testnet, parseAddress("0xcd520707fc68d153283d518b29ada466f9091ea8"), "KDO", true),
|
newErc721Contract("kudos", Network.Testnet, parseAddress("0xcd520707fc68d153283d518b29ada466f9091ea8"), "KDO", true),
|
||||||
Contract(name: "ens-usernames", network: Network.Testnet, address: parseAddress("0x11d9F481effd20D76cEE832559bd9Aca25405841"),
|
Contract(name: "ens-usernames", network: Network.Testnet, address: parseAddress("0x11d9F481effd20D76cEE832559bd9Aca25405841"),
|
||||||
methods: [
|
methods: [
|
||||||
("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")),
|
("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")),
|
||||||
("getPrice", Method(signature: "getPrice()"))
|
("getPrice", Method(signature: "getPrice()"))
|
||||||
].toTable
|
].toTable
|
||||||
),
|
),
|
||||||
Contract(name: "ens-resolver", network: Network.Testnet, address: parseAddress("0x42D63ae25990889E35F215bC95884039Ba354115"),
|
Contract(name: "ens-resolver", network: Network.Testnet, address: parseAddress("0x42D63ae25990889E35F215bC95884039Ba354115"),
|
||||||
methods: [
|
methods: [
|
||||||
("setPubkey", Method(signature: "setPubkey(bytes32,bytes32,bytes32)"))
|
("setPubkey", Method(signature: "setPubkey(bytes32,bytes32,bytes32)"))
|
||||||
].toTable
|
].toTable
|
||||||
),
|
),
|
||||||
|
|
||||||
# Rinkeby contracts
|
# Rinkeby contracts
|
||||||
newErc20Contract("Moksha Coin", Network.Rinkeby, parseAddress("0x6ba7dc8dd10880ab83041e60c4ede52bb607864b"), "MOKSHA", 18, false),
|
newErc20Contract("Moksha Coin", Network.Rinkeby, parseAddress("0x6ba7dc8dd10880ab83041e60c4ede52bb607864b"), "MOKSHA", 18, false),
|
||||||
newErc20Contract("WIBB", Network.Rinkeby, parseAddress("0x7d4ccf6af2f0fdad48ee7958bcc28bdef7b732c7"), "WIBB", 18, false),
|
newErc20Contract("WIBB", Network.Rinkeby, parseAddress("0x7d4ccf6af2f0fdad48ee7958bcc28bdef7b732c7"), "WIBB", 18, false),
|
||||||
newErc20Contract("Status Test Token", Network.Rinkeby, parseAddress("0x43d5adc3b49130a575ae6e4b00dfa4bc55c71621"), "STT", 18, false),
|
newErc20Contract("Status Test Token", Network.Rinkeby, parseAddress("0x43d5adc3b49130a575ae6e4b00dfa4bc55c71621"), "STT", 18, false),
|
||||||
|
|
||||||
# xDai contracts
|
# xDai contracts
|
||||||
newErc20Contract("buffiDai", Network.XDai, parseAddress("0x3e50bf6703fc132a94e4baff068db2055655f11b"), "BUFF", 18, false),
|
newErc20Contract("buffiDai", Network.XDai, parseAddress("0x3e50bf6703fc132a94e4baff068db2055655f11b"), "BUFF", 18, false),
|
||||||
|
|
||||||
newErc20Contract("Uniswap", Network.Mainnet, parseAddress("0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"), "UNI", 18, true),
|
newErc20Contract("Uniswap", Network.Mainnet, parseAddress("0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"), "UNI", 18, true),
|
||||||
newErc20Contract("Compound", Network.Mainnet, parseAddress("0xc00e94cb662c3520282e6f5717214004a7f26888"), "COMP", 18, true),
|
newErc20Contract("Compound", Network.Mainnet, parseAddress("0xc00e94cb662c3520282e6f5717214004a7f26888"), "COMP", 18, true),
|
||||||
newErc20Contract("Balancer", Network.Mainnet, parseAddress("0xba100000625a3754423978a60c9317c58a424e3d"), "BAL", 18, true),
|
newErc20Contract("Balancer", Network.Mainnet, parseAddress("0xba100000625a3754423978a60c9317c58a424e3d"), "BAL", 18, true),
|
||||||
newErc20Contract("Akropolis", Network.Mainnet, parseAddress("0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7"), "AKRO", 18, true),
|
newErc20Contract("Akropolis", Network.Mainnet, parseAddress("0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7"), "AKRO", 18, true),
|
||||||
newErc20Contract("Orchid", Network.Mainnet, parseAddress("0x4575f41308EC1483f3d399aa9a2826d74Da13Deb"), "OXT", 18, false),
|
newErc20Contract("Orchid", Network.Mainnet, parseAddress("0x4575f41308EC1483f3d399aa9a2826d74Da13Deb"), "OXT", 18, false),
|
||||||
]
|
]
|
||||||
|
contractsInited = true
|
||||||
|
result = contracts
|
||||||
|
|
||||||
proc getContract(network: Network, name: string): Contract =
|
proc getContract(network: Network, name: string): Contract =
|
||||||
{.gcsafe.}:
|
let found = allContracts().filter(contract => contract.name == name and contract.network == network)
|
||||||
withLock contractsLock:
|
result = if found.len > 0: found[0] else: nil
|
||||||
let found = ALL_CONTRACTS.filter(contract => contract.name == name and contract.network == network)
|
|
||||||
result = if found.len > 0: found[0] else: nil
|
|
||||||
|
|
||||||
proc getContract*(name: string): Contract =
|
proc getContract*(name: string): Contract =
|
||||||
let network = settings.getCurrentNetwork()
|
let network = settings.getCurrentNetwork()
|
||||||
|
@ -275,27 +279,19 @@ proc getErc20ContractByAddress*(contracts: seq[Erc20Contract], address: Address)
|
||||||
|
|
||||||
proc getErc20Contract*(symbol: string): Erc20Contract =
|
proc getErc20Contract*(symbol: string): Erc20Contract =
|
||||||
let network = settings.getCurrentNetwork()
|
let network = settings.getCurrentNetwork()
|
||||||
{.gcsafe.}:
|
result = allContracts().filter(contract => contract.network == network and contract of Erc20Contract).map(contract => Erc20Contract(contract)).getErc20ContractBySymbol(symbol)
|
||||||
withLock contractsLock:
|
|
||||||
result = ALL_CONTRACTS.filter(contract => contract.network == network and contract of Erc20Contract).map(contract => Erc20Contract(contract)).getErc20ContractBySymbol(symbol)
|
|
||||||
|
|
||||||
proc getErc20Contract*(address: Address): Erc20Contract =
|
proc getErc20Contract*(address: Address): Erc20Contract =
|
||||||
let network = settings.getCurrentNetwork()
|
let network = settings.getCurrentNetwork()
|
||||||
{.gcsafe.}:
|
result = allContracts().filter(contract => contract.network == network and contract of Erc20Contract).map(contract => Erc20Contract(contract)).getErc20ContractByAddress(address)
|
||||||
withLock contractsLock:
|
|
||||||
result = ALL_CONTRACTS.filter(contract => contract.network == network and contract of Erc20Contract).map(contract => Erc20Contract(contract)).getErc20ContractByAddress(address)
|
|
||||||
|
|
||||||
proc getErc20Contracts*(): seq[Erc20Contract] =
|
proc getErc20Contracts*(): seq[Erc20Contract] =
|
||||||
let network = settings.getCurrentNetwork()
|
let network = settings.getCurrentNetwork()
|
||||||
{.gcsafe.}:
|
result = allContracts().filter(contract => contract of Erc20Contract and contract.network == network).map(contract => Erc20Contract(contract))
|
||||||
withLock contractsLock:
|
|
||||||
result = ALL_CONTRACTS.filter(contract => contract of Erc20Contract and contract.network == network).map(contract => Erc20Contract(contract))
|
|
||||||
|
|
||||||
proc getErc721Contract(network: Network, name: string): Erc721Contract =
|
proc getErc721Contract(network: Network, name: string): Erc721Contract =
|
||||||
{.gcsafe.}:
|
let found = allContracts().filter(contract => contract of Erc721Contract and Erc721Contract(contract).name.toLower == name.toLower and contract.network == network)
|
||||||
withLock contractsLock:
|
result = if found.len > 0: Erc721Contract(found[0]) else: nil
|
||||||
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
|
|
||||||
|
|
||||||
proc getErc721Contract*(name: string): Erc721Contract =
|
proc getErc721Contract*(name: string): Erc721Contract =
|
||||||
let network = settings.getCurrentNetwork()
|
let network = settings.getCurrentNetwork()
|
||||||
|
@ -303,9 +299,7 @@ proc getErc721Contract*(name: string): Erc721Contract =
|
||||||
|
|
||||||
proc getErc721Contracts*(): seq[Erc721Contract] =
|
proc getErc721Contracts*(): seq[Erc721Contract] =
|
||||||
let network = settings.getCurrentNetwork()
|
let network = settings.getCurrentNetwork()
|
||||||
{.gcsafe.}:
|
result = allContracts().filter(contract => contract of Erc721Contract and contract.network == network).map(contract => Erc721Contract(contract))
|
||||||
withLock contractsLock:
|
|
||||||
result = ALL_CONTRACTS.filter(contract => contract of Erc721Contract and contract.network == network).map(contract => Erc721Contract(contract))
|
|
||||||
|
|
||||||
proc getSntContract*(): Erc20Contract =
|
proc getSntContract*(): Erc20Contract =
|
||||||
if settings.getCurrentNetwork() == Network.Mainnet:
|
if settings.getCurrentNetwork() == Network.Mainnet:
|
||||||
|
|
|
@ -1,37 +1,46 @@
|
||||||
import core, ./types, ../signals/types as statusgo_types, ./accounts/constants, ./utils
|
import
|
||||||
import json, tables, sugar, sequtils, strutils
|
json, tables, sugar, sequtils, strutils, atomics
|
||||||
import json_serialization
|
|
||||||
import locks
|
|
||||||
import uuids
|
|
||||||
|
|
||||||
var settingsLock {.global.}: Lock
|
import
|
||||||
initLock(settingsLock)
|
json_serialization, chronicles, uuids
|
||||||
|
|
||||||
var settings = %*{}
|
import
|
||||||
var dirty = true
|
./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 =
|
proc saveSetting*(key: Setting, value: string | JsonNode): StatusGoError =
|
||||||
withLock settingsLock:
|
try:
|
||||||
try:
|
let response = callPrivateRPC("settings_saveSetting", %* [key, value])
|
||||||
let response = callPrivateRPC("settings_saveSetting", %* [key, value])
|
let responseResult = $(response.parseJSON(){"result"})
|
||||||
result = Json.decode($response, StatusGoError)
|
if responseResult == "null":
|
||||||
except:
|
result.error = ""
|
||||||
dirty = true
|
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 =
|
proc getWeb3ClientVersion*(): string =
|
||||||
parseJson(callPrivateRPC("web3_clientVersion"))["result"].getStr
|
parseJson(callPrivateRPC("web3_clientVersion"))["result"].getStr
|
||||||
|
|
||||||
proc getSettings*(useCached: bool = true, keepSensitiveData: bool = false): JsonNode =
|
proc getSettings*(useCached: bool = true, keepSensitiveData: bool = false): JsonNode =
|
||||||
withLock settingsLock:
|
let cacheIsDirty = (not settingsInited) or dirty.load
|
||||||
{.gcsafe.}:
|
if useCached and (not cacheIsDirty) and (not keepSensitiveData):
|
||||||
if useCached and not dirty and not keepSensitiveData:
|
result = settings
|
||||||
result = settings
|
else:
|
||||||
else:
|
result = callPrivateRPC("settings_getSettings").parseJSON()["result"]
|
||||||
result = callPrivateRPC("settings_getSettings").parseJSON()["result"]
|
if not keepSensitiveData:
|
||||||
if not keepSensitiveData:
|
dirty.store(false)
|
||||||
dirty = false
|
delete(result, "mnemonic")
|
||||||
delete(result, "mnemonic")
|
settings = result
|
||||||
settings = result
|
settingsInited = true
|
||||||
|
|
||||||
proc getSetting*[T](name: Setting, defaultValue: T, useCached: bool = true): T =
|
proc getSetting*[T](name: Setting, defaultValue: T, useCached: bool = true): T =
|
||||||
let settings: JsonNode = getSettings(useCached, $name == "mnemonic")
|
let settings: JsonNode = getSettings(useCached, $name == "mnemonic")
|
||||||
|
|
|
@ -47,7 +47,8 @@ proc decodeContentHash*(value: string): string =
|
||||||
# 20 = multihash length = 32
|
# 20 = multihash length = 32
|
||||||
# ...rest = multihash digest
|
# ...rest = multihash digest
|
||||||
let multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get()
|
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
|
trace "Decoded sticker hash", cid=result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Error decoding sticker", hash=value, exception=e.msg
|
error "Error decoding sticker", hash=value, exception=e.msg
|
||||||
|
@ -59,14 +60,14 @@ proc decodeContentHash*(value: string): string =
|
||||||
proc getBalance*(address: Address): int =
|
proc getBalance*(address: Address): int =
|
||||||
let contract = contracts.getContract("sticker-pack")
|
let contract = contracts.getContract("sticker-pack")
|
||||||
if contract == nil: return 0
|
if contract == nil: return 0
|
||||||
|
|
||||||
let
|
let
|
||||||
balanceOf = BalanceOf(address: address)
|
balanceOf = BalanceOf(address: address)
|
||||||
payload = %* [{
|
payload = %* [{
|
||||||
"to": $contract.address,
|
"to": $contract.address,
|
||||||
"data": contract.methods["balanceOf"].encodeAbi(balanceOf)
|
"data": contract.methods["balanceOf"].encodeAbi(balanceOf)
|
||||||
}, "latest"]
|
}, "latest"]
|
||||||
|
|
||||||
let responseStr = status.callPrivateRPC("eth_call", payload)
|
let responseStr = status.callPrivateRPC("eth_call", payload)
|
||||||
let response = Json.decode(responseStr, RpcResponse)
|
let response = Json.decode(responseStr, RpcResponse)
|
||||||
if not response.error.isNil:
|
if not response.error.isNil:
|
||||||
|
@ -84,7 +85,7 @@ proc getPackCount*(): int =
|
||||||
"to": $contract.address,
|
"to": $contract.address,
|
||||||
"data": contract.methods["packCount"].encodeAbi()
|
"data": contract.methods["packCount"].encodeAbi()
|
||||||
}, "latest"]
|
}, "latest"]
|
||||||
|
|
||||||
let responseStr = status.callPrivateRPC("eth_call", payload)
|
let responseStr = status.callPrivateRPC("eth_call", payload)
|
||||||
let response = Json.decode(responseStr, RpcResponse)
|
let response = Json.decode(responseStr, RpcResponse)
|
||||||
if not response.error.isNil:
|
if not response.error.isNil:
|
||||||
|
@ -95,39 +96,38 @@ proc getPackCount*(): int =
|
||||||
|
|
||||||
# Gets sticker pack data
|
# Gets sticker pack data
|
||||||
proc getPackData*(id: Stuint[256]): StickerPack =
|
proc getPackData*(id: Stuint[256]): StickerPack =
|
||||||
{.gcsafe.}:
|
let
|
||||||
let
|
contract = contracts.getContract("stickers")
|
||||||
contract = contracts.getContract("stickers")
|
contractMethod = contract.methods["getPackData"]
|
||||||
contractMethod = contract.methods["getPackData"]
|
getPackData = GetPackData(packId: id)
|
||||||
getPackData = GetPackData(packId: id)
|
payload = %* [{
|
||||||
payload = %* [{
|
"to": $contract.address,
|
||||||
"to": $contract.address,
|
"data": contractMethod.encodeAbi(getPackData)
|
||||||
"data": contractMethod.encodeAbi(getPackData)
|
}, "latest"]
|
||||||
}, "latest"]
|
let responseStr = status.callPrivateRPC("eth_call", payload)
|
||||||
let responseStr = status.callPrivateRPC("eth_call", payload)
|
let response = Json.decode(responseStr, RpcResponse)
|
||||||
let response = Json.decode(responseStr, RpcResponse)
|
if not response.error.isNil:
|
||||||
if not response.error.isNil:
|
raise newException(RpcException, "Error getting sticker pack data: " & response.error.message)
|
||||||
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
|
# contract response includes a contenthash, which needs to be decoded to reveal
|
||||||
# an IPFS identifier. Once decoded, download the content from IPFS. This content
|
# an IPFS identifier. Once decoded, download the content from IPFS. This content
|
||||||
# is in EDN format, ie https://ipfs.infura.io/ipfs/QmWVVLwVKCwkVNjYJrRzQWREVvEk917PhbHYAUhA1gECTM
|
# is in EDN format, ie https://ipfs.infura.io/ipfs/QmWVVLwVKCwkVNjYJrRzQWREVvEk917PhbHYAUhA1gECTM
|
||||||
# and it also needs to be decoded in to a nim type
|
# and it also needs to be decoded in to a nim type
|
||||||
let secureSSLContext = newContext()
|
let secureSSLContext = newContext()
|
||||||
let client = newHttpClient(sslContext = secureSSLContext)
|
let client = newHttpClient(sslContext = secureSSLContext)
|
||||||
let contentHash = contracts.toHex(packData.contentHash)
|
let contentHash = contracts.toHex(packData.contentHash)
|
||||||
let url = "https://ipfs.infura.io/ipfs/" & decodeContentHash(contentHash)
|
let url = "https://ipfs.infura.io/ipfs/" & decodeContentHash(contentHash)
|
||||||
var ednMeta = client.getContent(url)
|
var ednMeta = client.getContent(url)
|
||||||
|
|
||||||
# decode the EDN content in to a StickerPack
|
# decode the EDN content in to a StickerPack
|
||||||
result = edn_helpers.decode[StickerPack](ednMeta)
|
result = edn_helpers.decode[StickerPack](ednMeta)
|
||||||
# EDN doesn't include a packId for each sticker, so add it here
|
# EDN doesn't include a packId for each sticker, so add it here
|
||||||
result.stickers.apply(proc(sticker: var Sticker) =
|
result.stickers.apply(proc(sticker: var Sticker) =
|
||||||
sticker.packId = truncate(id, int))
|
sticker.packId = truncate(id, int))
|
||||||
result.id = truncate(id, int)
|
result.id = truncate(id, int)
|
||||||
result.price = packData.price
|
result.price = packData.price
|
||||||
|
|
||||||
proc tokenOfOwnerByIndex*(address: Address, idx: Stuint[256]): int =
|
proc tokenOfOwnerByIndex*(address: Address, idx: Stuint[256]): int =
|
||||||
let
|
let
|
||||||
|
@ -137,7 +137,7 @@ proc tokenOfOwnerByIndex*(address: Address, idx: Stuint[256]): int =
|
||||||
"to": $contract.address,
|
"to": $contract.address,
|
||||||
"data": contract.methods["tokenOfOwnerByIndex"].encodeAbi(tokenOfOwnerByIndex)
|
"data": contract.methods["tokenOfOwnerByIndex"].encodeAbi(tokenOfOwnerByIndex)
|
||||||
}, "latest"]
|
}, "latest"]
|
||||||
|
|
||||||
let responseStr = status.callPrivateRPC("eth_call", payload)
|
let responseStr = status.callPrivateRPC("eth_call", payload)
|
||||||
let response = Json.decode(responseStr, RpcResponse)
|
let response = Json.decode(responseStr, RpcResponse)
|
||||||
if not response.error.isNil:
|
if not response.error.isNil:
|
||||||
|
@ -154,7 +154,7 @@ proc getPackIdFromTokenId*(tokenId: Stuint[256]): int =
|
||||||
"to": $contract.address,
|
"to": $contract.address,
|
||||||
"data": contract.methods["tokenPackId"].encodeAbi(tokenPackId)
|
"data": contract.methods["tokenPackId"].encodeAbi(tokenPackId)
|
||||||
}, "latest"]
|
}, "latest"]
|
||||||
|
|
||||||
let responseStr = status.callPrivateRPC("eth_call", payload)
|
let responseStr = status.callPrivateRPC("eth_call", payload)
|
||||||
let response = Json.decode(responseStr, RpcResponse)
|
let response = Json.decode(responseStr, RpcResponse)
|
||||||
if not response.error.isNil:
|
if not response.error.isNil:
|
||||||
|
@ -202,7 +202,7 @@ proc getRecentStickers*(): seq[Sticker] =
|
||||||
|
|
||||||
proc getAvailableStickerPacks*(): Table[int, StickerPack] =
|
proc getAvailableStickerPacks*(): Table[int, StickerPack] =
|
||||||
var availableStickerPacks = initTable[int, StickerPack]()
|
var availableStickerPacks = initTable[int, StickerPack]()
|
||||||
try:
|
try:
|
||||||
let numPacks = getPackCount()
|
let numPacks = getPackCount()
|
||||||
for i in 0..<numPacks:
|
for i in 0..<numPacks:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,38 +1,40 @@
|
||||||
import json, chronicles, strformat, stint, strutils, sequtils, tables
|
import
|
||||||
import core, wallet
|
json, chronicles, strformat, stint, strutils, sequtils, tables, atomics
|
||||||
import ./eth/contracts
|
|
||||||
import web3/[ethtypes, conversions]
|
import
|
||||||
import json_serialization
|
web3/[ethtypes, conversions], json_serialization
|
||||||
import settings
|
|
||||||
|
import
|
||||||
|
./settings, ./core, ./wallet, ./eth/contracts
|
||||||
from types import Setting, Network, RpcResponse, RpcException
|
from types import Setting, Network, RpcResponse, RpcException
|
||||||
from utils import parseAddress
|
from utils import parseAddress
|
||||||
import locks
|
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "wallet"
|
topics = "wallet"
|
||||||
|
|
||||||
var customTokensLock: Lock
|
var
|
||||||
initLock(customTokensLock)
|
customTokens {.threadvar.}: seq[Erc20Contract]
|
||||||
|
customTokensInited {.threadvar.}: bool
|
||||||
|
dirty: Atomic[bool]
|
||||||
|
|
||||||
var customTokens {.guard: customTokensLock.}: seq[Erc20Contract] = @[]
|
dirty.store(true)
|
||||||
var dirty {.guard: customTokensLock.} = true
|
|
||||||
|
|
||||||
proc getCustomTokens*(useCached: bool = true): seq[Erc20Contract] =
|
proc getCustomTokens*(useCached: bool = true): seq[Erc20Contract] =
|
||||||
{.gcsafe.}:
|
let cacheIsDirty = not customTokensInited or dirty.load
|
||||||
withLock customTokensLock:
|
if useCached and not cacheIsDirty:
|
||||||
if useCached and not dirty:
|
result = customTokens
|
||||||
result = customTokens
|
else:
|
||||||
else:
|
let payload = %* []
|
||||||
let payload = %* []
|
let responseStr = callPrivateRPC("wallet_getCustomTokens", payload)
|
||||||
let responseStr = callPrivateRPC("wallet_getCustomTokens", payload)
|
# TODO: this should be handled in the deserialisation of RpcResponse,
|
||||||
# TODO: this should be handled in the deserialisation of RpcResponse,
|
# question has been posed: https://discordapp.com/channels/613988663034118151/616299964242460682/762828178624217109
|
||||||
# question has been posed: https://discordapp.com/channels/613988663034118151/616299964242460682/762828178624217109
|
let response = RpcResponse(result: $(responseStr.parseJSON()["result"]))
|
||||||
let response = RpcResponse(result: $(responseStr.parseJSON()["result"]))
|
if not response.error.isNil:
|
||||||
if not response.error.isNil:
|
raise newException(RpcException, "Error getting custom tokens: " & response.error.message)
|
||||||
raise newException(RpcException, "Error getting custom tokens: " & response.error.message)
|
result = if response.result == "null": @[] else: Json.decode(response.result, seq[Erc20Contract])
|
||||||
result = if response.result == "null": @[] else: Json.decode(response.result, seq[Erc20Contract])
|
dirty.store(false)
|
||||||
dirty = false
|
customTokens = result
|
||||||
customTokens = result
|
customTokensInited = true
|
||||||
|
|
||||||
proc visibleTokensSNTDefault(): JsonNode =
|
proc visibleTokensSNTDefault(): JsonNode =
|
||||||
let currentNetwork = getCurrentNetwork()
|
let currentNetwork = getCurrentNetwork()
|
||||||
|
@ -85,14 +87,12 @@ proc getVisibleTokens*(): seq[Erc20Contract] =
|
||||||
proc addCustomToken*(address: string, name: string, symbol: string, decimals: int, color: string) =
|
proc addCustomToken*(address: string, name: string, symbol: string, decimals: int, color: string) =
|
||||||
let payload = %* [{"address": address, "name": name, "symbol": symbol, "decimals": decimals, "color": color}]
|
let payload = %* [{"address": address, "name": name, "symbol": symbol, "decimals": decimals, "color": color}]
|
||||||
discard callPrivateRPC("wallet_addCustomToken", payload)
|
discard callPrivateRPC("wallet_addCustomToken", payload)
|
||||||
withLock customTokensLock:
|
dirty.store(true)
|
||||||
dirty = true
|
|
||||||
|
|
||||||
proc removeCustomToken*(address: string) =
|
proc removeCustomToken*(address: string) =
|
||||||
let payload = %* [address]
|
let payload = %* [address]
|
||||||
echo callPrivateRPC("wallet_deleteCustomToken", payload)
|
echo callPrivateRPC("wallet_deleteCustomToken", payload)
|
||||||
withLock customTokensLock:
|
dirty.store(true)
|
||||||
dirty = true
|
|
||||||
|
|
||||||
proc getTokensBalances*(accounts: openArray[string], tokens: openArray[string]): JsonNode =
|
proc getTokensBalances*(accounts: openArray[string], tokens: openArray[string]): JsonNode =
|
||||||
let payload = %* [accounts, tokens]
|
let payload = %* [accounts, tokens]
|
||||||
|
|
|
@ -4,6 +4,7 @@ import libstatus/settings as libstatus_settings
|
||||||
import libstatus/types as libstatus_types
|
import libstatus/types as libstatus_types
|
||||||
import chat, accounts, wallet, node, network, mailservers, messages, contacts, profile, stickers, permissions, fleet
|
import chat, accounts, wallet, node, network, mailservers, messages, contacts, profile, stickers, permissions, fleet
|
||||||
import ../eventemitter
|
import ../eventemitter
|
||||||
|
import tasks/task_manager
|
||||||
|
|
||||||
export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions, fleet
|
export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions, fleet
|
||||||
|
|
||||||
|
@ -21,9 +22,11 @@ type Status* = ref object
|
||||||
network*: NetworkModel
|
network*: NetworkModel
|
||||||
stickers*: StickersModel
|
stickers*: StickersModel
|
||||||
permissions*: PermissionsModel
|
permissions*: PermissionsModel
|
||||||
|
taskManager*: TaskManager
|
||||||
|
|
||||||
proc newStatusInstance*(fleetConfig: string): Status =
|
proc newStatusInstance*(taskManager: TaskManager, fleetConfig: string): Status =
|
||||||
result = Status()
|
result = Status()
|
||||||
|
result.taskManager = taskManager
|
||||||
result.events = createEventEmitter()
|
result.events = createEventEmitter()
|
||||||
result.fleet = fleet.newFleetModel(result.events, fleetConfig)
|
result.fleet = fleet.newFleetModel(result.events, fleetConfig)
|
||||||
result.chat = chat.newChatModel(result.events)
|
result.chat = chat.newChatModel(result.events)
|
||||||
|
|
|
@ -42,7 +42,7 @@ proc init*(self: StickersModel) =
|
||||||
var evArgs = StickerArgs(e)
|
var evArgs = StickerArgs(e)
|
||||||
self.addStickerToRecent(evArgs.sticker, evArgs.save)
|
self.addStickerToRecent(evArgs.sticker, evArgs.save)
|
||||||
|
|
||||||
proc buildTransaction(self: StickersModel, packId: Uint256, address: Address, price: Uint256, approveAndCall: var ApproveAndCall[100], sntContract: var Erc20Contract, gas = "", gasPrice = ""): EthSend =
|
proc buildTransaction(packId: Uint256, address: Address, price: Uint256, approveAndCall: var ApproveAndCall[100], sntContract: var Erc20Contract, gas = "", gasPrice = ""): EthSend =
|
||||||
sntContract = status_contracts.getSntContract()
|
sntContract = status_contracts.getSntContract()
|
||||||
let
|
let
|
||||||
stickerMktContract = status_contracts.getContract("sticker-market")
|
stickerMktContract = status_contracts.getContract("sticker-market")
|
||||||
|
@ -51,11 +51,11 @@ proc buildTransaction(self: StickersModel, packId: Uint256, address: Address, pr
|
||||||
approveAndCall = ApproveAndCall[100](to: stickerMktContract.address, value: price, data: DynamicBytes[100].fromHex(buyTxAbiEncoded))
|
approveAndCall = ApproveAndCall[100](to: stickerMktContract.address, value: price, data: DynamicBytes[100].fromHex(buyTxAbiEncoded))
|
||||||
transactions.buildTokenTransaction(address, sntContract.address, gas, gasPrice)
|
transactions.buildTokenTransaction(address, sntContract.address, gas, gasPrice)
|
||||||
|
|
||||||
proc estimateGas*(self: StickersModel, packId: int, address: string, price: string, success: var bool): int =
|
proc estimateGas*(packId: int, address: string, price: string, success: var bool): int =
|
||||||
var
|
var
|
||||||
approveAndCall: ApproveAndCall[100]
|
approveAndCall: ApproveAndCall[100]
|
||||||
sntContract = status_contracts.getSntContract()
|
sntContract = status_contracts.getSntContract()
|
||||||
tx = self.buildTransaction(
|
tx = buildTransaction(
|
||||||
packId.u256,
|
packId.u256,
|
||||||
parseAddress(address),
|
parseAddress(address),
|
||||||
eth2Wei(parseFloat(price), sntContract.decimals),
|
eth2Wei(parseFloat(price), sntContract.decimals),
|
||||||
|
@ -71,7 +71,7 @@ proc buyPack*(self: StickersModel, packId: int, address, price, gas, gasPrice, p
|
||||||
var
|
var
|
||||||
sntContract: Erc20Contract
|
sntContract: Erc20Contract
|
||||||
approveAndCall: ApproveAndCall[100]
|
approveAndCall: ApproveAndCall[100]
|
||||||
tx = self.buildTransaction(
|
tx = buildTransaction(
|
||||||
packId.u256,
|
packId.u256,
|
||||||
parseAddress(address),
|
parseAddress(address),
|
||||||
eth2Wei(parseFloat(price), 18), # SNT
|
eth2Wei(parseFloat(price), 18), # SNT
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import
|
||||||
|
chronos, NimQml, json_serialization, task_runner
|
||||||
|
|
||||||
|
import
|
||||||
|
../stickers
|
||||||
|
|
||||||
|
type
|
||||||
|
StickerPackPurchaseGasEstimate* = object
|
||||||
|
vptr*: ByteAddress
|
||||||
|
slot*: string
|
||||||
|
packId*: int
|
||||||
|
address*: string
|
||||||
|
price*: string
|
||||||
|
uuid*: string
|
||||||
|
StickersTasks* = ref object
|
||||||
|
chanSendToPool: AsyncChannel[ThreadSafeString]
|
||||||
|
|
||||||
|
proc newStickersTasks*(chanSendToPool: AsyncChannel[ThreadSafeString]): StickersTasks =
|
||||||
|
new(result)
|
||||||
|
result.chanSendToPool = chanSendToPool
|
||||||
|
|
||||||
|
proc runTask*(stickerPackPurchaseGasEstimate: StickerPackPurchaseGasEstimate) =
|
||||||
|
var success: bool
|
||||||
|
var estimate = estimateGas(
|
||||||
|
stickerPackPurchaseGasEstimate.packId,
|
||||||
|
stickerPackPurchaseGasEstimate.address,
|
||||||
|
stickerPackPurchaseGasEstimate.price,
|
||||||
|
success
|
||||||
|
)
|
||||||
|
if not success:
|
||||||
|
estimate = 325000
|
||||||
|
let result: tuple[estimate: int, uuid: string] = (estimate, stickerPackPurchaseGasEstimate.uuid)
|
||||||
|
let resultPayload = Json.encode(result)
|
||||||
|
|
||||||
|
signal_handler(cast[pointer](stickerPackPurchaseGasEstimate.vptr), resultPayload, stickerPackPurchaseGasEstimate.slot)
|
||||||
|
|
||||||
|
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)
|
||||||
|
let payload = task.toJson(typeAnnotations = true)
|
||||||
|
self.chanSendToPool.sendSync(payload.safe)
|
|
@ -0,0 +1,27 @@
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,256 @@
|
||||||
|
import
|
||||||
|
chronicles, chronos, json, json_serialization, NimQml, sequtils, tables,
|
||||||
|
task_runner
|
||||||
|
|
||||||
|
import
|
||||||
|
./stickers
|
||||||
|
export
|
||||||
|
stickers
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "task-threadpool"
|
||||||
|
|
||||||
|
type
|
||||||
|
ThreadPool* = ref object
|
||||||
|
chanRecvFromPool*: AsyncChannel[ThreadSafeString]
|
||||||
|
chanSendToPool*: AsyncChannel[ThreadSafeString]
|
||||||
|
thread: Thread[PoolThreadArg]
|
||||||
|
size: int
|
||||||
|
stickers*: StickersTasks
|
||||||
|
PoolThreadArg* = object
|
||||||
|
chanSendToMain*: AsyncChannel[ThreadSafeString]
|
||||||
|
chanRecvFromMain*: AsyncChannel[ThreadSafeString]
|
||||||
|
size*: int
|
||||||
|
TaskThreadArg = object
|
||||||
|
id: int
|
||||||
|
chanRecvFromPool: AsyncChannel[ThreadSafeString]
|
||||||
|
chanSendToPool: AsyncChannel[ThreadSafeString]
|
||||||
|
ThreadNotification = object
|
||||||
|
id: int
|
||||||
|
notice: string
|
||||||
|
|
||||||
|
|
||||||
|
# forward declarations
|
||||||
|
proc poolThread(arg: PoolThreadArg) {.thread.}
|
||||||
|
|
||||||
|
const MaxThreadPoolSize = 16
|
||||||
|
|
||||||
|
proc newThreadPool*(size: int = MaxThreadPoolSize): ThreadPool =
|
||||||
|
new(result)
|
||||||
|
result.chanRecvFromPool = newAsyncChannel[ThreadSafeString](-1)
|
||||||
|
result.chanSendToPool = newAsyncChannel[ThreadSafeString](-1)
|
||||||
|
result.thread = Thread[PoolThreadArg]()
|
||||||
|
result.size = size
|
||||||
|
result.stickers = newStickersTasks(result.chanSendToPool)
|
||||||
|
|
||||||
|
proc init*(self: ThreadPool) =
|
||||||
|
self.chanRecvFromPool.open()
|
||||||
|
self.chanSendToPool.open()
|
||||||
|
let arg = PoolThreadArg(
|
||||||
|
chanSendToMain: self.chanRecvFromPool,
|
||||||
|
chanRecvFromMain: self.chanSendToPool,
|
||||||
|
size: self.size
|
||||||
|
)
|
||||||
|
createThread(self.thread, poolThread, arg)
|
||||||
|
|
||||||
|
# block until we receive "ready"
|
||||||
|
let received = $(self.chanRecvFromPool.recvSync())
|
||||||
|
|
||||||
|
proc teardown*(self: ThreadPool) =
|
||||||
|
self.chanSendToPool.sendSync("shutdown".safe)
|
||||||
|
self.chanRecvFromPool.close()
|
||||||
|
self.chanSendToPool.close()
|
||||||
|
joinThread(self.thread)
|
||||||
|
|
||||||
|
proc task(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)
|
||||||
|
|
||||||
|
while true:
|
||||||
|
info "[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"
|
||||||
|
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,
|
||||||
|
threadid=arg.id
|
||||||
|
|
||||||
|
try:
|
||||||
|
case messageType
|
||||||
|
of "StickerPackPurchaseGasEstimate":
|
||||||
|
let decoded = Json.decode(received, StickerPackPurchaseGasEstimate, allowUnknownFields = true)
|
||||||
|
decoded.runTask()
|
||||||
|
else:
|
||||||
|
error "[threadpool task thread] unknown message", message=received
|
||||||
|
except Exception as e:
|
||||||
|
error "[threadpool task thread] exception", error=e.msg
|
||||||
|
|
||||||
|
let noticeToPool = ThreadNotification(id: arg.id, notice: "done")
|
||||||
|
info "[threadpool task thread] sending 'done' notice to pool",
|
||||||
|
threadid=arg.id
|
||||||
|
await arg.chanSendToPool.send(noticeToPool.toJson(typeAnnotations = true).safe)
|
||||||
|
|
||||||
|
arg.chanRecvFromPool.close()
|
||||||
|
arg.chanSendToPool.close()
|
||||||
|
|
||||||
|
proc taskThread(arg: TaskThreadArg) {.thread.} =
|
||||||
|
waitFor task(arg)
|
||||||
|
|
||||||
|
proc pool(arg: PoolThreadArg) {.async.} =
|
||||||
|
let
|
||||||
|
chanSendToMain = arg.chanSendToMain
|
||||||
|
chanRecvFromMainOrTask = arg.chanRecvFromMain
|
||||||
|
var threadsBusy = newTable[int, tuple[thr: Thread[TaskThreadArg],
|
||||||
|
chanSendToTask: AsyncChannel[ThreadSafeString]]]()
|
||||||
|
var threadsIdle = newSeq[tuple[id: int, thr: Thread[TaskThreadArg],
|
||||||
|
chanSendToTask: AsyncChannel[ThreadSafeString]]](arg.size)
|
||||||
|
var taskQueue: seq[string] = @[] # FIFO queue
|
||||||
|
var allReady = 0
|
||||||
|
chanSendToMain.open()
|
||||||
|
chanRecvFromMainOrTask.open()
|
||||||
|
|
||||||
|
info "[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
|
||||||
|
threadsIdle[i].id = id
|
||||||
|
createThread(
|
||||||
|
threadsIdle[i].thr,
|
||||||
|
taskThread,
|
||||||
|
TaskThreadArg(id: id, chanRecvFromPool: chanSendToTask,
|
||||||
|
chanSendToPool: chanRecvFromMainOrTask
|
||||||
|
)
|
||||||
|
)
|
||||||
|
threadsIdle[i].chanSendToTask = chanSendToTask
|
||||||
|
|
||||||
|
# when task received and number of busy threads == MaxThreadPoolSize,
|
||||||
|
# then put the task in a queue
|
||||||
|
|
||||||
|
# when task received and number of busy threads < MaxThreadPoolSize, pop
|
||||||
|
# a thread from threadsIdle, track that thread in threadsBusy, and run
|
||||||
|
# task in that thread
|
||||||
|
|
||||||
|
# if "done" received from a thread, remove thread from threadsBusy, and
|
||||||
|
# push thread into threadsIdle
|
||||||
|
|
||||||
|
while true:
|
||||||
|
info "[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"
|
||||||
|
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
|
||||||
|
|
||||||
|
case messageType
|
||||||
|
of "ThreadNotification":
|
||||||
|
try:
|
||||||
|
let notification = Json.decode(task, ThreadNotification, allowUnknownFields = true)
|
||||||
|
info "[threadpool] received notification",
|
||||||
|
notice=notification.notice, threadid=notification.id
|
||||||
|
|
||||||
|
if notification.notice == "ready":
|
||||||
|
info "[threadpool] received 'ready' from a task thread"
|
||||||
|
allReady = allReady + 1
|
||||||
|
|
||||||
|
elif notification.notice == "done":
|
||||||
|
let tpl = threadsBusy[notification.id]
|
||||||
|
info "[threadpool] adding to threadsIdle",
|
||||||
|
newlength=(threadsIdle.len + 1)
|
||||||
|
threadsIdle.add (notification.id, tpl.thr, tpl.chanSendToTask)
|
||||||
|
info "[threadpool] removing from threadsBusy",
|
||||||
|
newlength=(threadsBusy.len - 1), threadid=notification.id
|
||||||
|
threadsBusy.del notification.id
|
||||||
|
|
||||||
|
if taskQueue.len > 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)
|
|
@ -349,3 +349,18 @@ proc getGasPricePredictions*(self: WalletModel): GasPricePrediction =
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
echo "error getting gas price predictions"
|
echo "error getting gas price predictions"
|
||||||
echo e.msg
|
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
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 1cc16aaa5c643d0d33c31f9953fbe4a9f6bde151
|
Subproject commit 5177a129d45985bb61471d7afecd397a82ed9d42
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4cda60880e108f0cb7efe1c209308f2db03267d9
|
Subproject commit 3305e41f9da3f2f21c56bd23b74b0a3589f3bf3e
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8d9e231a74c1afc76b6745e05020f8d4e33501e7
|
Subproject commit 70deac9e0d16f7d00a0d4404ed191042dbf079be
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit a87f3f85be052fb3332358f95079a059cf1daf15
|
Loading…
Reference in New Issue