chore(@desktop/wallet2): wallet2 controller added
Wallet2 related classes added to `src/status` and `src/status/wallet2`. `src/app/wallet/v2` classes updated accordingly.
This commit is contained in:
parent
71360d4362
commit
2177e06d95
|
@ -4,12 +4,12 @@ import view
|
||||||
import views/[account_list, account_item]
|
import views/[account_list, account_item]
|
||||||
import ../../../status/types as status_types
|
import ../../../status/types as status_types
|
||||||
import ../../../status/signals/types
|
import ../../../status/signals/types
|
||||||
import ../../../status/[status, wallet, settings]
|
import ../../../status/[status, wallet2, settings]
|
||||||
import ../../../status/wallet/account as WalletTypes
|
import ../../../status/wallet2/account as WalletTypes
|
||||||
import ../../../eventemitter
|
import ../../../eventemitter
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "wallet-core"
|
topics = "app-wallet2"
|
||||||
|
|
||||||
type WalletController* = ref object
|
type WalletController* = ref object
|
||||||
status: Status
|
status: Status
|
||||||
|
@ -27,7 +27,8 @@ proc delete*(self: WalletController) =
|
||||||
delete self.view
|
delete self.view
|
||||||
|
|
||||||
proc init*(self: WalletController) =
|
proc init*(self: WalletController) =
|
||||||
var accounts = self.status.wallet.accounts
|
self.status.wallet2.init()
|
||||||
|
var accounts = self.status.wallet2.getAccounts()
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
self.view.addAccountToList(account)
|
self.view.addAccountToList(account)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables
|
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables
|
||||||
import NimQml, chronicles, stint
|
import NimQml, chronicles, stint
|
||||||
|
|
||||||
import
|
import ../../../status/[status, wallet2]
|
||||||
../../../status/[status, wallet],
|
import views/[accounts, account_list, collectibles]
|
||||||
views/[accounts, account_list, collectibles]
|
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
@ -27,7 +26,9 @@ QtObject:
|
||||||
result.collectiblesView = newCollectiblesView(status)
|
result.collectiblesView = newCollectiblesView(status)
|
||||||
result.setup
|
result.setup
|
||||||
|
|
||||||
proc getAccounts(self: WalletView): QVariant {.slot.} = newQVariant(self.accountsView)
|
proc getAccounts(self: WalletView): QVariant {.slot.} =
|
||||||
|
newQVariant(self.accountsView)
|
||||||
|
|
||||||
QtProperty[QVariant] accountsView:
|
QtProperty[QVariant] accountsView:
|
||||||
read = getAccounts
|
read = getAccounts
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import NimQml, std/wrapnils, strformat, options
|
import NimQml, std/wrapnils, strformat, options
|
||||||
from ../../../../status/wallet import WalletAccount
|
from ../../../../status/wallet2 import WalletAccount
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type AccountItemView* = ref object of QObject
|
type AccountItemView* = ref object of QObject
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import NimQml, Tables, random, strformat, strutils, json_serialization
|
import NimQml, Tables, random, strformat, strutils, json_serialization
|
||||||
import sequtils as sequtils
|
import sequtils as sequtils
|
||||||
import account_item
|
import account_item
|
||||||
from ../../../../status/wallet import WalletAccount, Asset, CollectibleList
|
from ../../../../status/wallet2 import WalletAccount, Asset, CollectibleList
|
||||||
|
|
||||||
const accountColors* = ["#9B832F", "#D37EF4", "#1D806F", "#FA6565", "#7CDA00", "#887af9", "#8B3131"]
|
const accountColors* = ["#9B832F", "#D37EF4", "#1D806F", "#FA6565", "#7CDA00", "#887af9", "#8B3131"]
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ import NimQml, json, sequtils, chronicles, strutils, strformat, json
|
||||||
import
|
import
|
||||||
../../../../status/[status, settings, types],
|
../../../../status/[status, settings, types],
|
||||||
../../../../status/signals/types as signal_types,
|
../../../../status/signals/types as signal_types,
|
||||||
../../../../status/wallet as status_wallet
|
../../../../status/wallet2 as status_wallet
|
||||||
|
|
||||||
import account_list, account_item
|
import account_list, account_item
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "accounts-view"
|
topics = "app-wallet2-accounts-view"
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type AccountsView* = ref object of QObject
|
type AccountsView* = ref object of QObject
|
||||||
|
@ -34,24 +34,24 @@ QtObject:
|
||||||
|
|
||||||
proc generateNewAccount*(self: AccountsView, password: string, accountName: string, color: string): string {.slot.} =
|
proc generateNewAccount*(self: AccountsView, password: string, accountName: string, color: string): string {.slot.} =
|
||||||
try:
|
try:
|
||||||
self.status.wallet.generateNewAccount(password, accountName, color)
|
self.status.wallet2.generateNewAccount(password, accountName, color)
|
||||||
except StatusGoException as e:
|
except StatusGoException as e:
|
||||||
result = StatusGoError(error: e.msg).toJson
|
result = StatusGoError(error: e.msg).toJson
|
||||||
|
|
||||||
proc addAccountsFromSeed*(self: AccountsView, seed: string, password: string, accountName: string, color: string): string {.slot.} =
|
proc addAccountsFromSeed*(self: AccountsView, seed: string, password: string, accountName: string, color: string): string {.slot.} =
|
||||||
try:
|
try:
|
||||||
self.status.wallet.addAccountsFromSeed(seed.strip(), password, accountName, color)
|
self.status.wallet2.addAccountsFromSeed(seed.strip(), password, accountName, color)
|
||||||
except StatusGoException as e:
|
except StatusGoException as e:
|
||||||
result = StatusGoError(error: e.msg).toJson
|
result = StatusGoError(error: e.msg).toJson
|
||||||
|
|
||||||
proc addAccountsFromPrivateKey*(self: AccountsView, privateKey: string, password: string, accountName: string, color: string): string {.slot.} =
|
proc addAccountsFromPrivateKey*(self: AccountsView, privateKey: string, password: string, accountName: string, color: string): string {.slot.} =
|
||||||
try:
|
try:
|
||||||
self.status.wallet.addAccountsFromPrivateKey(privateKey, password, accountName, color)
|
self.status.wallet2.addAccountsFromPrivateKey(privateKey, password, accountName, color)
|
||||||
except StatusGoException as e:
|
except StatusGoException as e:
|
||||||
result = StatusGoError(error: e.msg).toJson
|
result = StatusGoError(error: e.msg).toJson
|
||||||
|
|
||||||
proc addWatchOnlyAccount*(self: AccountsView, address: string, accountName: string, color: string): string {.slot.} =
|
proc addWatchOnlyAccount*(self: AccountsView, address: string, accountName: string, color: string): string {.slot.} =
|
||||||
self.status.wallet.addWatchOnlyAccount(address, accountName, color)
|
self.status.wallet2.addWatchOnlyAccount(address, accountName, color)
|
||||||
|
|
||||||
proc currentAccountChanged*(self: AccountsView) {.signal.}
|
proc currentAccountChanged*(self: AccountsView) {.signal.}
|
||||||
|
|
||||||
|
@ -62,14 +62,14 @@ QtObject:
|
||||||
self.accountListChanged()
|
self.accountListChanged()
|
||||||
|
|
||||||
proc changeAccountSettings*(self: AccountsView, address: string, accountName: string, color: string): string {.slot.} =
|
proc changeAccountSettings*(self: AccountsView, address: string, accountName: string, color: string): string {.slot.} =
|
||||||
result = self.status.wallet.changeAccountSettings(address, accountName, color)
|
result = self.status.wallet2.changeAccountSettings(address, accountName, color)
|
||||||
if (result == ""):
|
if (result == ""):
|
||||||
self.currentAccountChanged()
|
self.currentAccountChanged()
|
||||||
self.accountListChanged()
|
self.accountListChanged()
|
||||||
self.accounts.forceUpdate()
|
self.accounts.forceUpdate()
|
||||||
|
|
||||||
proc deleteAccount*(self: AccountsView, address: string): string {.slot.} =
|
proc deleteAccount*(self: AccountsView, address: string): string {.slot.} =
|
||||||
result = self.status.wallet.deleteAccount(address)
|
result = self.status.wallet2.deleteAccount(address)
|
||||||
if (result == ""):
|
if (result == ""):
|
||||||
let index = self.accounts.getAccountindexByAddress(address)
|
let index = self.accounts.getAccountindexByAddress(address)
|
||||||
if (index == -1):
|
if (index == -1):
|
||||||
|
@ -127,7 +127,7 @@ QtObject:
|
||||||
self.currentAccount.address
|
self.currentAccount.address
|
||||||
|
|
||||||
proc setAccountItems*(self: AccountsView) =
|
proc setAccountItems*(self: AccountsView) =
|
||||||
for account in self.status.wallet.accounts:
|
for account in self.status.wallet2.getAccounts():
|
||||||
if account.address == self.currentAccount.address:
|
if account.address == self.currentAccount.address:
|
||||||
self.currentAccount.setAccountItem(account)
|
self.currentAccount.setAccountItem(account)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import NimQml, Tables
|
import NimQml, Tables
|
||||||
from ../../../../status/wallet import OpenseaAsset
|
from ../../../../status/wallet2 import OpenseaAsset
|
||||||
|
|
||||||
type
|
type
|
||||||
AssetRoles {.pure.} = enum
|
AssetRoles {.pure.} = enum
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import NimQml, Tables, json, chronicles
|
import NimQml, Tables, json, chronicles
|
||||||
|
|
||||||
import
|
import
|
||||||
../../../../status/[status, wallet],
|
../../../../status/[status, wallet2],
|
||||||
../../../../status/tasks/[qt, task_runner_impl]
|
../../../../status/tasks/[qt, task_runner_impl]
|
||||||
|
|
||||||
import collection_list, asset_list
|
import collection_list, asset_list
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "collectibles-view"
|
topics = "app-wallet2-collectibles-view"
|
||||||
|
|
||||||
type
|
type
|
||||||
LoadCollectionsTaskArg = ref object of QObjectTaskArg
|
LoadCollectionsTaskArg = ref object of QObjectTaskArg
|
||||||
|
@ -15,7 +15,7 @@ type
|
||||||
|
|
||||||
const loadCollectionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
const loadCollectionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
let arg = decode[LoadCollectionsTaskArg](argEncoded)
|
let arg = decode[LoadCollectionsTaskArg](argEncoded)
|
||||||
let output = wallet.getOpenseaCollections(arg.address)
|
let output = wallet2.getOpenseaCollections(arg.address)
|
||||||
arg.finish(output)
|
arg.finish(output)
|
||||||
|
|
||||||
proc loadCollections[T](self: T, slot: string, address: string) =
|
proc loadCollections[T](self: T, slot: string, address: string) =
|
||||||
|
@ -36,7 +36,7 @@ const loadAssetsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
let arg = decode[LoadAssetsTaskArg](argEncoded)
|
let arg = decode[LoadAssetsTaskArg](argEncoded)
|
||||||
let output = %*{
|
let output = %*{
|
||||||
"collectionSlug": arg.collectionSlug,
|
"collectionSlug": arg.collectionSlug,
|
||||||
"assets": parseJson(wallet.getOpenseaAssets(arg.address, arg.collectionSlug, arg.limit)),
|
"assets": parseJson(wallet2.getOpenseaAssets(arg.address, arg.collectionSlug, arg.limit)),
|
||||||
}
|
}
|
||||||
arg.finish(output)
|
arg.finish(output)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import NimQml, Tables
|
import NimQml, Tables
|
||||||
from ../../../../status/wallet import OpenseaCollection
|
from ../../../../status/wallet2 import OpenseaCollection
|
||||||
|
|
||||||
type
|
type
|
||||||
CollectionRoles {.pure.} = enum
|
CollectionRoles {.pure.} = enum
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ../utils as utils
|
||||||
import ../types as types
|
import ../types as types
|
||||||
import accounts/constants
|
import accounts/constants
|
||||||
import ../signals/types as signal_types
|
import ../signals/types as signal_types
|
||||||
import ../wallet/account
|
|
||||||
|
|
||||||
proc getNetworkConfig(currentNetwork: string): JsonNode =
|
proc getNetworkConfig(currentNetwork: string): JsonNode =
|
||||||
result = constants.DEFAULT_NETWORKS.first("id", currentNetwork)
|
result = constants.DEFAULT_NETWORKS.first("id", currentNetwork)
|
||||||
|
@ -323,15 +322,15 @@ proc saveAccount*(account: GeneratedAccount, password: string, color: string, ac
|
||||||
error "Error storing the new account. Bad password?"
|
error "Error storing the new account. Bad password?"
|
||||||
raise
|
raise
|
||||||
|
|
||||||
proc changeAccount*(account: WalletAccount): string =
|
proc changeAccount*(name, address, publicKey, walletType, iconColor: string): string =
|
||||||
try:
|
try:
|
||||||
let response = callPrivateRPC("accounts_saveAccounts", %* [
|
let response = callPrivateRPC("accounts_saveAccounts", %* [
|
||||||
[{
|
[{
|
||||||
"color": account.iconColor,
|
"color": iconColor,
|
||||||
"name": account.name,
|
"name": name,
|
||||||
"address": account.address,
|
"address": address,
|
||||||
"public-key": account.publicKey,
|
"public-key": publicKey,
|
||||||
"type": account.walletType,
|
"type": walletType,
|
||||||
"path": "m/44'/60'/0'/0/1" # <--- TODO: fix this. Derivation path is not supposed to change
|
"path": "m/44'/60'/0'/0/1" # <--- TODO: fix this. Derivation path is not supposed to change
|
||||||
}]
|
}]
|
||||||
])
|
])
|
||||||
|
|
|
@ -526,7 +526,6 @@ proc rpcPinnedChatMessages*(chatId: string, cursorVal: string, limit: int, succe
|
||||||
success = true
|
success = true
|
||||||
try:
|
try:
|
||||||
result = callPrivateRPC("chatPinnedMessages".prefix, %* [chatId, cursorVal, limit])
|
result = callPrivateRPC("chatPinnedMessages".prefix, %* [chatId, cursorVal, limit])
|
||||||
debug "chatPinnedMessages", result
|
|
||||||
except RpcException as e:
|
except RpcException as e:
|
||||||
success = false
|
success = false
|
||||||
result = e.msg
|
result = e.msg
|
||||||
|
|
|
@ -2,7 +2,7 @@ import libstatus/accounts as libstatus_accounts
|
||||||
import libstatus/core as libstatus_core
|
import libstatus/core as libstatus_core
|
||||||
import libstatus/settings as libstatus_settings
|
import libstatus/settings as libstatus_settings
|
||||||
import types as libstatus_types
|
import types as libstatus_types
|
||||||
import chat, accounts, wallet, node, network, messages, contacts, profile, stickers, permissions, fleet, settings, mailservers, browser, tokens, provider
|
import chat, accounts, wallet, wallet2, node, network, messages, contacts, profile, stickers, permissions, fleet, settings, mailservers, browser, tokens, provider
|
||||||
import notifications/os_notifications
|
import notifications/os_notifications
|
||||||
import ../eventemitter
|
import ../eventemitter
|
||||||
import ./tasks/task_runner_impl
|
import ./tasks/task_runner_impl
|
||||||
|
@ -17,6 +17,7 @@ type Status* = ref object
|
||||||
messages*: MessagesModel
|
messages*: MessagesModel
|
||||||
accounts*: AccountModel
|
accounts*: AccountModel
|
||||||
wallet*: WalletModel
|
wallet*: WalletModel
|
||||||
|
wallet2*: StatusWalletController
|
||||||
node*: NodeModel
|
node*: NodeModel
|
||||||
profile*: ProfileModel
|
profile*: ProfileModel
|
||||||
contacts*: ContactModel
|
contacts*: ContactModel
|
||||||
|
@ -40,6 +41,7 @@ proc newStatusInstance*(fleetConfig: string): Status =
|
||||||
result.accounts = accounts.newAccountModel(result.events)
|
result.accounts = accounts.newAccountModel(result.events)
|
||||||
result.wallet = wallet.newWalletModel(result.events)
|
result.wallet = wallet.newWalletModel(result.events)
|
||||||
result.wallet.initEvents()
|
result.wallet.initEvents()
|
||||||
|
result.wallet2 = wallet2.newStatusWalletController(result.events, result.tasks)
|
||||||
result.node = node.newNodeModel()
|
result.node = node.newNodeModel()
|
||||||
result.messages = messages.newMessagesModel(result.events)
|
result.messages = messages.newMessagesModel(result.events)
|
||||||
result.profile = profile.newProfileModel()
|
result.profile = profile.newProfileModel()
|
||||||
|
|
|
@ -322,7 +322,8 @@ proc changeAccountSettings*(self: WalletModel, address: string, accountName: str
|
||||||
error "No account found with that address", address
|
error "No account found with that address", address
|
||||||
selectedAccount.name = accountName
|
selectedAccount.name = accountName
|
||||||
selectedAccount.iconColor = color
|
selectedAccount.iconColor = color
|
||||||
result = status_accounts.changeAccount(selectedAccount)
|
result = status_accounts.changeAccount(selectedAccount.name, selectedAccount.address,
|
||||||
|
selectedAccount.publicKey, selectedAccount.walletType, selectedAccount.iconColor)
|
||||||
|
|
||||||
proc deleteAccount*(self: WalletModel, address: string): string =
|
proc deleteAccount*(self: WalletModel, address: string): string =
|
||||||
result = status_accounts.deleteAccount(address)
|
result = status_accounts.deleteAccount(address)
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
import NimQml
|
||||||
|
import json, strformat, options, chronicles, sugar, sequtils, strutils
|
||||||
|
|
||||||
|
import tasks/[qt, task_runner_impl]
|
||||||
|
import wallet2/[balance_manager, account, collectibles]
|
||||||
|
import ../eventemitter
|
||||||
|
|
||||||
|
from types import PendingTransactionType, GeneratedAccount, DerivedAccount,
|
||||||
|
Transaction, Setting, GasPricePrediction, `%`, StatusGoException, Network,
|
||||||
|
RpcResponse, RpcException
|
||||||
|
|
||||||
|
import libstatus/accounts as status_accounts
|
||||||
|
import libstatus/accounts/constants as constants
|
||||||
|
import libstatus/tokens as status_tokens
|
||||||
|
import libstatus/wallet as status_wallet
|
||||||
|
import libstatus/settings as status_settings
|
||||||
|
import libstatus/eth/[contracts]
|
||||||
|
|
||||||
|
from web3/ethtypes import Address
|
||||||
|
from web3/conversions import `$`
|
||||||
|
|
||||||
|
export account, collectibles
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "status-wallet2"
|
||||||
|
|
||||||
|
type
|
||||||
|
CryptoServicesArg* = ref object of Args
|
||||||
|
services*: JsonNode # an array
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type StatusWalletController* = ref object of QObject
|
||||||
|
events: EventEmitter
|
||||||
|
tasks: TaskRunner
|
||||||
|
accounts: seq[WalletAccount]
|
||||||
|
tokens: seq[Erc20Contract]
|
||||||
|
totalBalance*: float
|
||||||
|
|
||||||
|
# Forward declarations
|
||||||
|
proc initEvents*(self: StatusWalletController)
|
||||||
|
proc generateAccountConfiguredAssets*(self: StatusWalletController,
|
||||||
|
accountAddress: string): seq[Asset]
|
||||||
|
proc calculateTotalFiatBalance*(self: StatusWalletController)
|
||||||
|
|
||||||
|
proc setup(self: StatusWalletController, events: EventEmitter, tasks: TaskRunner) =
|
||||||
|
self.QObject.setup
|
||||||
|
self.events = events
|
||||||
|
self.tasks = tasks
|
||||||
|
self.accounts = @[]
|
||||||
|
self.tokens = @[]
|
||||||
|
self.totalBalance = 0.0
|
||||||
|
self.initEvents()
|
||||||
|
|
||||||
|
proc delete*(self: StatusWalletController) =
|
||||||
|
self.QObject.delete
|
||||||
|
|
||||||
|
proc newStatusWalletController*(events: EventEmitter, tasks: TaskRunner):
|
||||||
|
StatusWalletController =
|
||||||
|
result = StatusWalletController()
|
||||||
|
result.setup(events, tasks)
|
||||||
|
|
||||||
|
proc initTokens(self: StatusWalletController) =
|
||||||
|
self.tokens = status_tokens.getVisibleTokens()
|
||||||
|
|
||||||
|
proc initAccounts(self: StatusWalletController) =
|
||||||
|
let accounts = status_wallet.getWalletAccounts()
|
||||||
|
for acc in accounts:
|
||||||
|
var assets: seq[Asset] = self.generateAccountConfiguredAssets(acc.address)
|
||||||
|
var walletAccount = newWalletAccount(acc.name, acc.address, acc.iconColor,
|
||||||
|
acc.path, acc.walletType, acc.publicKey, acc.wallet, acc.chat, assets)
|
||||||
|
self.accounts.add(walletAccount)
|
||||||
|
|
||||||
|
proc init*(self: StatusWalletController) =
|
||||||
|
self.initTokens()
|
||||||
|
self.initAccounts()
|
||||||
|
|
||||||
|
proc initEvents*(self: StatusWalletController) =
|
||||||
|
self.events.on("currencyChanged") do(e: Args):
|
||||||
|
self.events.emit("accountsUpdated", Args())
|
||||||
|
|
||||||
|
self.events.on("newAccountAdded") do(e: Args):
|
||||||
|
self.calculateTotalFiatBalance()
|
||||||
|
|
||||||
|
proc getAccounts*(self: StatusWalletController): seq[WalletAccount] =
|
||||||
|
self.accounts
|
||||||
|
|
||||||
|
proc getDefaultCurrency*(self: StatusWalletController): string =
|
||||||
|
# TODO: this should come from a model? It is going to be used too in the
|
||||||
|
# profile section and ideally we should not call the settings more than once
|
||||||
|
status_settings.getSetting[string](Setting.Currency, "usd")
|
||||||
|
|
||||||
|
proc generateAccountConfiguredAssets*(self: StatusWalletController,
|
||||||
|
accountAddress: string): seq[Asset] =
|
||||||
|
var assets: seq[Asset] = @[]
|
||||||
|
var asset = Asset(name:"Ethereum", symbol: "ETH", value: "0.0",
|
||||||
|
fiatBalanceDisplay: "0.0", accountAddress: accountAddress)
|
||||||
|
assets.add(asset)
|
||||||
|
for token in self.tokens:
|
||||||
|
var symbol = token.symbol
|
||||||
|
var existingToken = Asset(name: token.name, symbol: symbol,
|
||||||
|
value: fmt"0.0", fiatBalanceDisplay: "$0.0", accountAddress: accountAddress,
|
||||||
|
address: $token.address)
|
||||||
|
assets.add(existingToken)
|
||||||
|
assets
|
||||||
|
|
||||||
|
proc calculateTotalFiatBalance*(self: StatusWalletController) =
|
||||||
|
self.totalBalance = 0.0
|
||||||
|
for account in self.accounts:
|
||||||
|
if account.realFiatBalance.isSome:
|
||||||
|
self.totalBalance += account.realFiatBalance.get()
|
||||||
|
|
||||||
|
proc newAccount*(self: StatusWalletController, walletType: string, derivationPath: string,
|
||||||
|
name: string, address: string, iconColor: string, balance: string,
|
||||||
|
publicKey: string): WalletAccount =
|
||||||
|
var assets: seq[Asset] = self.generateAccountConfiguredAssets(address)
|
||||||
|
var account = WalletAccount(name: name, path: derivationPath, walletType: walletType,
|
||||||
|
address: address, iconColor: iconColor, balance: none[string](), assetList: assets,
|
||||||
|
realFiatBalance: none[float](), publicKey: publicKey)
|
||||||
|
updateBalance(account, self.getDefaultCurrency())
|
||||||
|
account
|
||||||
|
|
||||||
|
proc addNewGeneratedAccount(self: StatusWalletController, generatedAccount: GeneratedAccount,
|
||||||
|
password: string, accountName: string, color: string, accountType: string,
|
||||||
|
isADerivedAccount = true, walletIndex: int = 0) =
|
||||||
|
try:
|
||||||
|
generatedAccount.name = accountName
|
||||||
|
var derivedAccount: DerivedAccount = status_accounts.saveAccount(generatedAccount,
|
||||||
|
password, color, accountType, isADerivedAccount, walletIndex)
|
||||||
|
var account = self.newAccount(accountType, derivedAccount.derivationPath,
|
||||||
|
accountName, derivedAccount.address, color, fmt"0.00 {self.getDefaultCurrency()}",
|
||||||
|
derivedAccount.publicKey)
|
||||||
|
|
||||||
|
self.accounts.add(account)
|
||||||
|
# wallet_checkRecentHistory is required to be called when a new account is
|
||||||
|
# added before wallet_getTransfersByAddress can be called. This is because
|
||||||
|
# wallet_checkRecentHistory populates the status-go db that
|
||||||
|
# wallet_getTransfersByAddress reads from
|
||||||
|
discard status_wallet.checkRecentHistory(self.accounts.map(account => account.address))
|
||||||
|
self.events.emit("newAccountAdded", AccountArgs(account: account))
|
||||||
|
except Exception as e:
|
||||||
|
raise newException(StatusGoException, fmt"Error adding new account: {e.msg}")
|
||||||
|
|
||||||
|
proc generateNewAccount*(self: StatusWalletController, password: string, accountName: string, color: string) =
|
||||||
|
let
|
||||||
|
walletRootAddress = status_settings.getSetting[string](Setting.WalletRootAddress, "")
|
||||||
|
walletIndex = status_settings.getSetting[int](Setting.LatestDerivedPath) + 1
|
||||||
|
loadedAccount = status_accounts.loadAccount(walletRootAddress, password)
|
||||||
|
derivedAccount = status_accounts.deriveWallet(loadedAccount.id, walletIndex)
|
||||||
|
generatedAccount = GeneratedAccount(
|
||||||
|
id: loadedAccount.id,
|
||||||
|
publicKey: derivedAccount.publicKey,
|
||||||
|
address: derivedAccount.address
|
||||||
|
)
|
||||||
|
|
||||||
|
# if we've gotten here, the password is ok (loadAccount requires a valid password)
|
||||||
|
# so no need to check for a valid password
|
||||||
|
self.addNewGeneratedAccount(generatedAccount, password, accountName, color, constants.GENERATED, true, walletIndex)
|
||||||
|
|
||||||
|
let statusGoResult = status_settings.saveSetting(Setting.LatestDerivedPath, $walletIndex)
|
||||||
|
if statusGoResult.error != "":
|
||||||
|
error "Error storing the latest wallet index", msg=statusGoResult.error
|
||||||
|
|
||||||
|
proc addAccountsFromSeed*(self: StatusWalletController, seed: string, password: string, accountName: string, color: string) =
|
||||||
|
let mnemonic = replace(seed, ',', ' ')
|
||||||
|
var generatedAccount = status_accounts.multiAccountImportMnemonic(mnemonic)
|
||||||
|
generatedAccount.derived = status_accounts.deriveAccounts(generatedAccount.id)
|
||||||
|
|
||||||
|
let
|
||||||
|
defaultAccount = status_accounts.getDefaultAccount()
|
||||||
|
isPasswordOk = status_accounts.verifyAccountPassword(defaultAccount, password)
|
||||||
|
if not isPasswordOk:
|
||||||
|
raise newException(StatusGoException, "Error generating new account: invalid password")
|
||||||
|
|
||||||
|
self.addNewGeneratedAccount(generatedAccount, password, accountName, color, constants.SEED)
|
||||||
|
|
||||||
|
proc addAccountsFromPrivateKey*(self: StatusWalletController, privateKey: string, password: string, accountName: string, color: string) =
|
||||||
|
let
|
||||||
|
generatedAccount = status_accounts.MultiAccountImportPrivateKey(privateKey)
|
||||||
|
defaultAccount = status_accounts.getDefaultAccount()
|
||||||
|
isPasswordOk = status_accounts.verifyAccountPassword(defaultAccount, password)
|
||||||
|
|
||||||
|
if not isPasswordOk:
|
||||||
|
raise newException(StatusGoException, "Error generating new account: invalid password")
|
||||||
|
|
||||||
|
self.addNewGeneratedAccount(generatedAccount, password, accountName, color, constants.KEY, false)
|
||||||
|
|
||||||
|
proc addWatchOnlyAccount*(self: StatusWalletController, address: string, accountName: string, color: string) =
|
||||||
|
let account = GeneratedAccount(address: address)
|
||||||
|
self.addNewGeneratedAccount(account, "", accountName, color, constants.WATCH, false)
|
||||||
|
|
||||||
|
proc changeAccountSettings*(self: StatusWalletController, address: string, accountName: string, color: string): string =
|
||||||
|
var selectedAccount: WalletAccount
|
||||||
|
for account in self.accounts:
|
||||||
|
if (account.address == address):
|
||||||
|
selectedAccount = account
|
||||||
|
break
|
||||||
|
if (isNil(selectedAccount)):
|
||||||
|
result = "No account found with that address"
|
||||||
|
error "No account found with that address", address
|
||||||
|
selectedAccount.name = accountName
|
||||||
|
selectedAccount.iconColor = color
|
||||||
|
result = status_accounts.changeAccount(selectedAccount.name, selectedAccount.address,
|
||||||
|
selectedAccount.publicKey, selectedAccount.walletType, selectedAccount.iconColor)
|
||||||
|
|
||||||
|
proc deleteAccount*(self: StatusWalletController, address: string): string =
|
||||||
|
result = status_accounts.deleteAccount(address)
|
||||||
|
self.accounts = self.accounts.filter(acc => acc.address.toLowerAscii != address.toLowerAscii)
|
||||||
|
|
||||||
|
proc getOpenseaCollections*(address: string): string =
|
||||||
|
result = status_wallet.getOpenseaCollections(address)
|
||||||
|
|
||||||
|
proc getOpenseaAssets*(address: string, collectionSlug: string, limit: int): string =
|
||||||
|
result = status_wallet.getOpenseaAssets(address, collectionSlug, limit)
|
|
@ -0,0 +1,77 @@
|
||||||
|
import options, json, strformat
|
||||||
|
|
||||||
|
from ../../eventemitter import Args
|
||||||
|
import ../types
|
||||||
|
|
||||||
|
type CollectibleList* = ref object
|
||||||
|
collectibleType*, collectiblesJSON*, error*: string
|
||||||
|
loading*: int
|
||||||
|
|
||||||
|
type Collectible* = ref object
|
||||||
|
name*, image*, id*, collectibleType*, description*, externalUrl*: string
|
||||||
|
|
||||||
|
type OpenseaCollection* = ref object
|
||||||
|
name*, slug*, imageUrl*: string
|
||||||
|
ownedAssetCount*: int
|
||||||
|
|
||||||
|
type OpenseaAsset* = ref object
|
||||||
|
id*: int
|
||||||
|
name*, description*, permalink*, imageThumbnailUrl*, imageUrl*, address*: string
|
||||||
|
|
||||||
|
type CurrencyArgs* = ref object of Args
|
||||||
|
currency*: string
|
||||||
|
|
||||||
|
type Asset* = ref object
|
||||||
|
name*, symbol*, value*, fiatBalanceDisplay*, fiatBalance*, accountAddress*, address*: string
|
||||||
|
|
||||||
|
type WalletAccount* = ref object
|
||||||
|
name*, address*, iconColor*, path*, walletType*, publicKey*: string
|
||||||
|
balance*: Option[string]
|
||||||
|
realFiatBalance*: Option[float]
|
||||||
|
assetList*: seq[Asset]
|
||||||
|
wallet*, chat*: bool
|
||||||
|
collectiblesLists*: seq[CollectibleList]
|
||||||
|
transactions*: tuple[hasMore: bool, data: seq[Transaction]]
|
||||||
|
|
||||||
|
proc newWalletAccount*(name, address, iconColor, path, walletType, publicKey: string,
|
||||||
|
wallet, chat: bool, assets: seq[Asset]): WalletAccount =
|
||||||
|
result = new WalletAccount
|
||||||
|
result.name = name
|
||||||
|
result.address = address
|
||||||
|
result.iconColor = iconColor
|
||||||
|
result.path = path
|
||||||
|
result.walletType = walletType
|
||||||
|
result.publicKey = publicKey
|
||||||
|
result.wallet = wallet
|
||||||
|
result.chat = chat
|
||||||
|
result.assetList = assets
|
||||||
|
result.balance = none[string]()
|
||||||
|
result.realFiatBalance = none[float]()
|
||||||
|
|
||||||
|
type AccountArgs* = ref object of Args
|
||||||
|
account*: WalletAccount
|
||||||
|
|
||||||
|
proc `$`*(self: OpenseaCollection): string =
|
||||||
|
return fmt"OpenseaCollection(name:{self.name}, slug:{self.slug}, owned asset count:{self.ownedAssetCount})"
|
||||||
|
|
||||||
|
proc `$`*(self: OpenseaAsset): string =
|
||||||
|
return fmt"OpenseaAsset(id:{self.id}, name:{self.name}, address:{self.address}, imageUrl: {self.imageUrl}, imageThumbnailUrl: {self.imageThumbnailUrl})"
|
||||||
|
|
||||||
|
proc toOpenseaCollection*(jsonCollection: JsonNode): OpenseaCollection =
|
||||||
|
return OpenseaCollection(
|
||||||
|
name: jsonCollection{"name"}.getStr,
|
||||||
|
slug: jsonCollection{"slug"}.getStr,
|
||||||
|
imageUrl: jsonCollection{"image_url"}.getStr,
|
||||||
|
ownedAssetCount: jsonCollection{"owned_asset_count"}.getInt
|
||||||
|
)
|
||||||
|
|
||||||
|
proc toOpenseaAsset*(jsonAsset: JsonNode): OpenseaAsset =
|
||||||
|
return OpenseaAsset(
|
||||||
|
id: jsonAsset{"id"}.getInt,
|
||||||
|
name: jsonAsset{"name"}.getStr,
|
||||||
|
description: jsonAsset{"description"}.getStr,
|
||||||
|
permalink: jsonAsset{"permalink"}.getStr,
|
||||||
|
imageThumbnailUrl: jsonAsset{"image_thumbnail_url"}.getStr,
|
||||||
|
imageUrl: jsonAsset{"image_url"}.getStr,
|
||||||
|
address: jsonAsset{"asset_contract"}{"address"}.getStr
|
||||||
|
)
|
|
@ -0,0 +1,90 @@
|
||||||
|
import strformat, strutils, stint, httpclient, json, chronicles, net
|
||||||
|
import ../libstatus/wallet as status_wallet
|
||||||
|
import ../libstatus/tokens as status_tokens
|
||||||
|
import ../types as status_types
|
||||||
|
import ../utils/cache
|
||||||
|
import account
|
||||||
|
import options
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "status-wallet2-balance-manager"
|
||||||
|
|
||||||
|
type BalanceManager* = ref object
|
||||||
|
pricePairs: CachedValues
|
||||||
|
tokenBalances: CachedValues
|
||||||
|
|
||||||
|
proc newBalanceManager*(): BalanceManager =
|
||||||
|
result = BalanceManager()
|
||||||
|
result.pricePairs = newCachedValues()
|
||||||
|
result.tokenBalances = newCachedValues()
|
||||||
|
|
||||||
|
var balanceManager = newBalanceManager()
|
||||||
|
|
||||||
|
proc getPrice(crypto: string, fiat: string): string =
|
||||||
|
let secureSSLContext = newContext()
|
||||||
|
let client = newHttpClient(sslContext = secureSSLContext)
|
||||||
|
try:
|
||||||
|
let url: string = fmt"https://min-api.cryptocompare.com/data/price?fsym={crypto}&tsyms={fiat}"
|
||||||
|
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||||
|
|
||||||
|
let response = client.request(url)
|
||||||
|
result = $parseJson(response.body)[fiat.toUpper]
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting price", message = e.msg
|
||||||
|
result = "0.0"
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
proc getEthBalance(address: string): string =
|
||||||
|
var balance = status_wallet.getBalance(address)
|
||||||
|
result = status_wallet.hex2token(balance, 18)
|
||||||
|
|
||||||
|
proc getBalance*(symbol: string, accountAddress: string, tokenAddress: string, refreshCache: bool): string =
|
||||||
|
let cacheKey = fmt"{symbol}-{accountAddress}-{tokenAddress}"
|
||||||
|
if not refreshCache and balanceManager.tokenBalances.isCached(cacheKey):
|
||||||
|
return balanceManager.tokenBalances.get(cacheKey)
|
||||||
|
|
||||||
|
if symbol == "ETH":
|
||||||
|
let ethBalance = getEthBalance(accountAddress)
|
||||||
|
return ethBalance
|
||||||
|
|
||||||
|
result = $status_tokens.getTokenBalance(tokenAddress, accountAddress)
|
||||||
|
balanceManager.tokenBalances.cacheValue(cacheKey, result)
|
||||||
|
|
||||||
|
proc convertValue*(balance: string, fromCurrency: string, toCurrency: string): float =
|
||||||
|
if balance == "0.0": return 0.0
|
||||||
|
let cacheKey = fmt"{fromCurrency}-{toCurrency}"
|
||||||
|
if balanceManager.pricePairs.isCached(cacheKey):
|
||||||
|
return parseFloat(balance) * parseFloat(balanceManager.pricePairs.get(cacheKey))
|
||||||
|
|
||||||
|
var fiat_crypto_price = getPrice(fromCurrency, toCurrency)
|
||||||
|
balanceManager.pricePairs.cacheValue(cacheKey, fiat_crypto_price)
|
||||||
|
parseFloat(balance) * parseFloat(fiat_crypto_price)
|
||||||
|
|
||||||
|
proc updateBalance*(asset: Asset, currency: string, refreshCache: bool): float =
|
||||||
|
var token_balance = getBalance(asset.symbol, asset.accountAddress, asset.address, refreshCache)
|
||||||
|
let fiat_balance = convertValue(token_balance, asset.symbol, currency)
|
||||||
|
asset.value = token_balance
|
||||||
|
asset.fiatBalanceDisplay = fmt"{fiat_balance:.2f} {currency}"
|
||||||
|
asset.fiatBalance = fmt"{fiat_balance:.2f}"
|
||||||
|
return fiat_balance
|
||||||
|
|
||||||
|
proc updateBalance*(account: WalletAccount, currency: string, refreshCache: bool = false) =
|
||||||
|
try:
|
||||||
|
var usd_balance = 0.0
|
||||||
|
for asset in account.assetList:
|
||||||
|
let assetFiatBalance = updateBalance(asset, currency, refreshCache)
|
||||||
|
usd_balance = usd_balance + assetFiatBalance
|
||||||
|
|
||||||
|
account.realFiatBalance = some(usd_balance)
|
||||||
|
account.balance = some(fmt"{usd_balance:.2f} {currency}")
|
||||||
|
except RpcException:
|
||||||
|
error "Error in updateBalance", message = getCurrentExceptionMsg()
|
||||||
|
|
||||||
|
proc storeBalances*(account: WalletAccount, ethBalance = "0", tokenBalance: JsonNode) =
|
||||||
|
let ethCacheKey = fmt"ETH-{account.address}-"
|
||||||
|
balanceManager.tokenBalances.cacheValue(ethCacheKey, ethBalance)
|
||||||
|
for asset in account.assetList:
|
||||||
|
if tokenBalance.hasKey(asset.address):
|
||||||
|
let cacheKey = fmt"{asset.symbol}-{account.address}-{asset.address}"
|
||||||
|
balanceManager.tokenBalances.cacheValue(cacheKey, tokenBalance{asset.address}.getStr())
|
|
@ -0,0 +1,259 @@
|
||||||
|
import # std libs
|
||||||
|
atomics, strformat, httpclient, json, chronicles, sequtils, strutils, tables,
|
||||||
|
sugar, net
|
||||||
|
|
||||||
|
import # vendor libs
|
||||||
|
stint
|
||||||
|
|
||||||
|
import # status-desktop libs
|
||||||
|
../libstatus/core as status, ../libstatus/eth/contracts as contracts,
|
||||||
|
../stickers as status_stickers, ../types,
|
||||||
|
web3/[conversions, ethtypes], ../utils, account
|
||||||
|
|
||||||
|
const CRYPTOKITTY* = "cryptokitty"
|
||||||
|
const KUDO* = "kudo"
|
||||||
|
const ETHERMON* = "ethermon"
|
||||||
|
const STICKER* = "stickers"
|
||||||
|
|
||||||
|
const COLLECTIBLE_TYPES* = [CRYPTOKITTY, KUDO, ETHERMON, STICKER]
|
||||||
|
|
||||||
|
const MAX_TOKENS = 200
|
||||||
|
|
||||||
|
proc getTokenUri(contract: Erc721Contract, tokenId: Stuint[256]): string =
|
||||||
|
try:
|
||||||
|
let
|
||||||
|
tokenUri = TokenUri(tokenId: tokenId)
|
||||||
|
payload = %* [{
|
||||||
|
"to": $contract.address,
|
||||||
|
"data": contract.methods["tokenURI"].encodeAbi(tokenUri)
|
||||||
|
}, "latest"]
|
||||||
|
response = callPrivateRPC("eth_call", payload)
|
||||||
|
var postfixedResult: string = parseJson($response)["result"].str
|
||||||
|
postfixedResult.removeSuffix('0')
|
||||||
|
postfixedResult.removePrefix("0x")
|
||||||
|
postfixedResult = parseHexStr(postfixedResult)
|
||||||
|
let index = postfixedResult.find("http")
|
||||||
|
if (index < -1):
|
||||||
|
return ""
|
||||||
|
result = postfixedResult[index .. postfixedResult.high]
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting the token URI", mes = e.msg
|
||||||
|
result = ""
|
||||||
|
|
||||||
|
proc tokenOfOwnerByIndex(contract: Erc721Contract, address: Address, index: Stuint[256]): int =
|
||||||
|
let
|
||||||
|
tokenOfOwnerByIndex = TokenOfOwnerByIndex(address: address, index: index)
|
||||||
|
payload = %* [{
|
||||||
|
"to": $contract.address,
|
||||||
|
"data": contract.methods["tokenOfOwnerByIndex"].encodeAbi(tokenOfOwnerByIndex)
|
||||||
|
}, "latest"]
|
||||||
|
response = callPrivateRPC("eth_call", payload)
|
||||||
|
jsonResponse = parseJson($response)
|
||||||
|
if (not jsonResponse.hasKey("result")):
|
||||||
|
return -1
|
||||||
|
let res = jsonResponse["result"].getStr
|
||||||
|
if (res == "0x"):
|
||||||
|
return -1
|
||||||
|
result = fromHex[int](res)
|
||||||
|
|
||||||
|
proc balanceOf(contract: Erc721Contract, address: Address): int =
|
||||||
|
let
|
||||||
|
balanceOf = BalanceOf(address: address)
|
||||||
|
payload = %* [{
|
||||||
|
"to": $contract.address,
|
||||||
|
"data": contract.methods["balanceOf"].encodeAbi(balanceOf)
|
||||||
|
}, "latest"]
|
||||||
|
response = callPrivateRPC("eth_call", payload)
|
||||||
|
jsonResponse = parseJson($response)
|
||||||
|
if (not jsonResponse.hasKey("result")):
|
||||||
|
return 0
|
||||||
|
let res = jsonResponse["result"].getStr
|
||||||
|
if (res == "0x"):
|
||||||
|
return 0
|
||||||
|
result = fromHex[int](res)
|
||||||
|
|
||||||
|
proc tokensOfOwnerByIndex(contract: Erc721Contract, address: Address): seq[int] =
|
||||||
|
var index = 0
|
||||||
|
var token: int
|
||||||
|
var maxIndex: int = balanceOf(contract, address)
|
||||||
|
result = @[]
|
||||||
|
while index < maxIndex and result.len <= MAX_TOKENS:
|
||||||
|
token = tokenOfOwnerByIndex(contract, address, index.u256)
|
||||||
|
result.add(token)
|
||||||
|
index = index + 1
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
proc getCryptoKittiesBatch*(address: Address, offset: int = 0): seq[Collectible] =
|
||||||
|
var cryptokitties: seq[Collectible]
|
||||||
|
cryptokitties = @[]
|
||||||
|
# TODO handle testnet -- does this API exist in testnet??
|
||||||
|
let url: string = fmt"https://api.cryptokitties.co/kitties?limit=20&offset={$offset}&owner_wallet_address={$address}&parents=false"
|
||||||
|
let secureSSLContext = newContext()
|
||||||
|
let client = newHttpClient(sslContext = secureSSLContext)
|
||||||
|
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||||
|
|
||||||
|
let response = client.request(url)
|
||||||
|
let responseBody = parseJson(response.body)
|
||||||
|
let kitties = responseBody["kitties"]
|
||||||
|
for kitty in kitties:
|
||||||
|
try:
|
||||||
|
var id = kitty["id"]
|
||||||
|
var name = kitty["name"]
|
||||||
|
var finalId = ""
|
||||||
|
var finalName = ""
|
||||||
|
if id.kind != JNull:
|
||||||
|
finalId = $id
|
||||||
|
if name.kind != JNull:
|
||||||
|
finalName = $name
|
||||||
|
cryptokitties.add(Collectible(id: finalId,
|
||||||
|
name: finalName,
|
||||||
|
image: kitty["image_url_png"].str,
|
||||||
|
collectibleType: CRYPTOKITTY,
|
||||||
|
description: "",
|
||||||
|
externalUrl: ""))
|
||||||
|
except Exception as e2:
|
||||||
|
error "Error with this individual cat", msg = e2.msg, cat = kitty
|
||||||
|
|
||||||
|
let limit = responseBody["limit"].getInt
|
||||||
|
let total = responseBody["total"].getInt
|
||||||
|
let currentCount = limit * (offset + 1)
|
||||||
|
if (currentCount < total and currentCount < MAX_TOKENS):
|
||||||
|
# Call the API again with offset + 1
|
||||||
|
let nextBatch = getCryptoKittiesBatch(address, offset + 1)
|
||||||
|
return concat(cryptokitties, nextBatch)
|
||||||
|
return cryptokitties
|
||||||
|
|
||||||
|
proc getCryptoKitties*(address: Address): string =
|
||||||
|
try:
|
||||||
|
let cryptokitties = getCryptoKittiesBatch(address, 0)
|
||||||
|
|
||||||
|
return $(%*cryptokitties)
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting Cryptokitties", msg = e.msg
|
||||||
|
return e.msg
|
||||||
|
|
||||||
|
proc getCryptoKitties*(address: string): string =
|
||||||
|
let eth_address = parseAddress(address)
|
||||||
|
result = getCryptoKitties(eth_address)
|
||||||
|
|
||||||
|
proc getEthermons*(address: Address): string =
|
||||||
|
try:
|
||||||
|
var ethermons: seq[Collectible]
|
||||||
|
ethermons = @[]
|
||||||
|
let contract = getErc721Contract("ethermon")
|
||||||
|
if contract == nil: return $(%*ethermons)
|
||||||
|
|
||||||
|
let tokens = tokensOfOwnerByIndex(contract, address)
|
||||||
|
|
||||||
|
if (tokens.len == 0):
|
||||||
|
return $(%*ethermons)
|
||||||
|
|
||||||
|
let tokensJoined = strutils.join(tokens, ",")
|
||||||
|
let url = fmt"https://www.ethermon.io/api/monster/get_data?monster_ids={tokensJoined}"
|
||||||
|
let secureSSLContext = newContext()
|
||||||
|
let client = newHttpClient(sslContext = secureSSLContext)
|
||||||
|
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||||
|
|
||||||
|
let response = client.request(url)
|
||||||
|
let monsters = parseJson(response.body)["data"]
|
||||||
|
var i = 0
|
||||||
|
for monsterKey in json.keys(monsters):
|
||||||
|
let monster = monsters[monsterKey]
|
||||||
|
ethermons.add(Collectible(id: $tokens[i],
|
||||||
|
name: monster["class_name"].str,
|
||||||
|
image: monster["image"].str,
|
||||||
|
collectibleType: ETHERMON,
|
||||||
|
description: "",
|
||||||
|
externalUrl: ""))
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
return $(%*ethermons)
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting Ethermons", msg = e.msg
|
||||||
|
result = e.msg
|
||||||
|
|
||||||
|
proc getEthermons*(address: string): string =
|
||||||
|
let eth_address = parseAddress(address)
|
||||||
|
result = getEthermons(eth_address)
|
||||||
|
|
||||||
|
proc getKudos*(address: Address): string =
|
||||||
|
try:
|
||||||
|
var kudos: seq[Collectible]
|
||||||
|
kudos = @[]
|
||||||
|
let contract = getErc721Contract("kudos")
|
||||||
|
if contract == nil: return $(%*kudos)
|
||||||
|
|
||||||
|
let tokens = tokensOfOwnerByIndex(contract, address)
|
||||||
|
|
||||||
|
if (tokens.len == 0):
|
||||||
|
return $(%*kudos)
|
||||||
|
|
||||||
|
for token in tokens:
|
||||||
|
let url = getTokenUri(contract, token.u256)
|
||||||
|
|
||||||
|
if (url == ""):
|
||||||
|
return $(%*kudos)
|
||||||
|
|
||||||
|
let secureSSLContext = newContext()
|
||||||
|
let client = newHttpClient(sslContext = secureSSLContext)
|
||||||
|
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||||
|
|
||||||
|
let response = client.request(url)
|
||||||
|
let kudo = parseJson(response.body)
|
||||||
|
|
||||||
|
kudos.add(Collectible(id: $token,
|
||||||
|
name: kudo["name"].str,
|
||||||
|
image: kudo["image"].str,
|
||||||
|
collectibleType: KUDO,
|
||||||
|
description: kudo["description"].str,
|
||||||
|
externalUrl: kudo["external_url"].str))
|
||||||
|
|
||||||
|
return $(%*kudos)
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting Kudos", msg = e.msg
|
||||||
|
result = e.msg
|
||||||
|
|
||||||
|
proc getKudos*(address: string): string =
|
||||||
|
let eth_address = parseAddress(address)
|
||||||
|
result = getKudos(eth_address)
|
||||||
|
|
||||||
|
proc getStickers*(address: Address, running: var Atomic[bool]): string =
|
||||||
|
try:
|
||||||
|
var stickers: seq[Collectible]
|
||||||
|
stickers = @[]
|
||||||
|
let contract = getErc721Contract("sticker-pack")
|
||||||
|
if contract == nil: return
|
||||||
|
|
||||||
|
let tokensIds = tokensOfOwnerByIndex(contract, address)
|
||||||
|
|
||||||
|
if (tokensIds.len == 0):
|
||||||
|
return $(%*stickers)
|
||||||
|
|
||||||
|
let purchasedStickerPacks = tokensIds.map(tokenId => status_stickers.getPackIdFromTokenId(tokenId.u256))
|
||||||
|
|
||||||
|
if (purchasedStickerPacks.len == 0):
|
||||||
|
return $(%*stickers)
|
||||||
|
# TODO find a way to keep those in memory so as not to reload it each time
|
||||||
|
let availableStickerPacks = getAvailableStickerPacks(running)
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
for stickerId in purchasedStickerPacks:
|
||||||
|
let sticker = availableStickerPacks[stickerId]
|
||||||
|
stickers.add(Collectible(id: $tokensIds[index],
|
||||||
|
name: sticker.name,
|
||||||
|
image: fmt"https://ipfs.infura.io/ipfs/{status_stickers.decodeContentHash(sticker.preview)}",
|
||||||
|
collectibleType: STICKER,
|
||||||
|
description: sticker.author,
|
||||||
|
externalUrl: "")
|
||||||
|
)
|
||||||
|
index = index + 1
|
||||||
|
|
||||||
|
return $(%*stickers)
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting Stickers", msg = e.msg
|
||||||
|
result = e.msg
|
||||||
|
|
||||||
|
proc getStickers*(address: string, running: var Atomic[bool]): string =
|
||||||
|
let eth_address = parseAddress(address)
|
||||||
|
result = getStickers(eth_address, running)
|
Loading…
Reference in New Issue