feat(@desktop/wallet2): implement buy crypto feature

If user wants to buy/sell crypto, popup modal list will be displayed so he may
choose which service to use for that action.

Fixes: #2120
This commit is contained in:
Sale Djenic 2021-09-06 14:12:38 +02:00 committed by Iuri Matias
parent 7973679926
commit 7bbfba7fb3
8 changed files with 255 additions and 2 deletions

View File

@ -43,3 +43,7 @@ proc init*(self: WalletController) =
self.status.events.on(SignalType.Wallet.event) do(e:Args):
var data = WalletSignal(e)
debug "TODO: handle wallet signal", signalType=data.eventType
self.status.events.on("cryptoServicesFetched") do(e: Args):
var args = CryptoServicesArg(e)
self.view.onCryptoServicesFetched(args.services)

View File

@ -3,6 +3,7 @@ import NimQml, chronicles, stint
import ../../../status/[status, wallet2]
import views/[accounts, account_list, collectibles]
import views/buy_sell_crypto/[service_controller]
QtObject:
type
@ -10,10 +11,12 @@ QtObject:
status: Status
accountsView: AccountsView
collectiblesView: CollectiblesView
cryptoServiceController: CryptoServiceController
proc delete(self: WalletView) =
self.accountsView.delete
self.collectiblesView.delete
self.cryptoServiceController.delete
self.QAbstractListModel.delete
proc setup(self: WalletView) =
@ -24,6 +27,7 @@ QtObject:
result.status = status
result.accountsView = newAccountsView(status)
result.collectiblesView = newCollectiblesView(status)
result.cryptoServiceController = newCryptoServiceController(status)
result.setup
proc getAccounts(self: WalletView): QVariant {.slot.} =
@ -53,3 +57,12 @@ QtObject:
# If it's the first account we ever get, use its list as our first lists
if (self.accountsView.accounts.rowCount == 1):
self.setCurrentAccountByIndex(0)
proc getCryptoServiceController*(self: WalletView): QVariant {.slot.} =
newQVariant(self.cryptoServiceController)
QtProperty[QVariant] cryptoServiceController:
read = getCryptoServiceController
proc onCryptoServicesFetched*(self: WalletView, jsonNode: JsonNode) =
self.cryptoServiceController.onCryptoServicesFetched(jsonNode)

View File

@ -0,0 +1,56 @@
import NimQml, json, strutils, chronicles
import service_model, service_item
import ../../../../../status/[status, wallet2]
logScope:
topics = "app-wallet2-crypto-service"
QtObject:
type CryptoServiceController* = ref object of QObject
status: Status
cryptoServiceModel: CryptoServiceModel
servicesFetched: bool
proc setup(self: CryptoServiceController) =
self.QObject.setup
proc delete*(self: CryptoServiceController) =
self.cryptoServiceModel.delete
self.QObject.delete
proc newCryptoServiceController*(status: Status): CryptoServiceController =
new(result, delete)
result.status = status
result.cryptoServiceModel = newCryptoServiceModel()
result.servicesFetched = false
result.setup
proc getCryptoServiceModel(self: CryptoServiceController): QVariant {.slot.} =
newQVariant(self.cryptoServiceModel)
QtProperty[QVariant] cryptoServiceModel:
read = getCryptoServiceModel
proc fetchCryptoServicesFetched*(self:CryptoServiceController) {.signal.}
proc fetchCryptoServices*(self: CryptoServiceController) {.slot.} =
if(not self.servicesFetched):
self.status.wallet2.asyncFetchCryptoServices()
else:
self.fetchCryptoServicesFetched()
proc onCryptoServicesFetched*(self: CryptoServiceController, jsonNode: JsonNode) =
self.servicesFetched = true
if (jsonNode.kind != JArray):
info "received crypto services list is empty"
else:
var items: seq[CryptoServiceItem] = @[]
for itemObject in jsonNode:
items.add(initCryptoServiceItem(itemObject))
self.cryptoServiceModel.set(items)
self.fetchCryptoServicesFetched()

View File

@ -0,0 +1,65 @@
import json, strformat, chronicles
include ../../../../../status/utils/[json_utils]
logScope:
topics = "app-wallet2-crypto-service"
type CryptoServiceItem* = object
name: string
description: string
fees: string
logoUrl: string
siteUrl: string
hostname: string
proc initCryptoServiceItem*(name, description, fees, logoUrl, siteUrl,
hostname: string): CryptoServiceItem =
result.name = name
result.description = description
result.fees = fees
result.logoUrl = logoUrl
result.siteUrl = siteUrl
result.hostname = hostname
proc initCryptoServiceItem*(jsonObject: JsonNode): CryptoServiceItem =
if (jsonObject.kind != JObject):
info "CryptoServiceItem initialization failed: JsonNode is not JObject"
return
discard jsonObject.getProp("name", result.name)
discard jsonObject.getProp("description", result.description)
discard jsonObject.getProp("fees", result.fees)
discard jsonObject.getProp("logoUrl", result.logoUrl)
discard jsonObject.getProp("siteUrl", result.siteUrl)
discard jsonObject.getProp("hostname", result.hostname)
proc `$`*(self: CryptoServiceItem): string =
result = "CryptoServiceItem("
result &= fmt"name:{self.name}, "
result &= fmt"description:{self.description}, "
result &= fmt"fees:{self.fees}, "
result &= fmt"logoUrl:{self.logoUrl}, "
result &= fmt"siteUrl:{self.siteUrl}"
result &= fmt"hostname:{self.hostname}"
result &= ")"
method getName*(self: CryptoServiceItem): string {.base.} =
return self.name
method getDescription*(self: CryptoServiceItem): string {.base.} =
return self.description
method getFees*(self: CryptoServiceItem): string {.base.} =
return self.fees
method getLogoUrl*(self: CryptoServiceItem): string {.base.} =
return self.logoUrl
method getSiteUrl*(self: CryptoServiceItem): string {.base.} =
return self.siteUrl
method getHostname*(self: CryptoServiceItem): string {.base.} =
return self.hostname

View File

@ -0,0 +1,69 @@
import NimQml, Tables
import service_item
type
CryptoServiceModelRole {.pure.} = enum
Name = UserRole + 1
Description
Fees
LogoUrl
SiteUrl
Hostname
QtObject:
type
CryptoServiceModel* = ref object of QAbstractListModel
list: seq[CryptoServiceItem]
proc delete(self: CryptoServiceModel) =
self.QAbstractListModel.delete
proc setup(self: CryptoServiceModel) =
self.QAbstractListModel.setup
proc newCryptoServiceModel*(): CryptoServiceModel =
new(result, delete)
result.setup()
method rowCount(self: CryptoServiceModel, index: QModelIndex = nil): int =
return self.list.len
method roleNames(self: CryptoServiceModel): Table[int, string] =
{
CryptoServiceModelRole.Name.int:"name",
CryptoServiceModelRole.Description.int:"description",
CryptoServiceModelRole.Fees.int:"fees",
CryptoServiceModelRole.LogoUrl.int:"logoUrl",
CryptoServiceModelRole.SiteUrl.int:"siteUrl",
CryptoServiceModelRole.Hostname.int:"hostname"
}.toTable
method data(self: CryptoServiceModel, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.list.len):
return
let item = self.list[index.row]
let enumRole = role.CryptoServiceModelRole
case enumRole:
of CryptoServiceModelRole.Name:
result = newQVariant(item.getName)
of CryptoServiceModelRole.Description:
result = newQVariant(item.getDescription)
of CryptoServiceModelRole.Fees:
result = newQVariant(item.getFees)
of CryptoServiceModelRole.LogoUrl:
result = newQVariant(item.getLogoUrl)
of CryptoServiceModelRole.SiteUrl:
result = newQVariant(item.getSiteUrl)
of CryptoServiceModelRole.Hostname:
result = newQVariant(item.getHostname)
proc set*(self: CryptoServiceModel, items: seq[CryptoServiceItem]) =
self.beginResetModel()
self.list = items
self.endResetModel()

View File

@ -135,4 +135,13 @@ proc getOpenseaCollections*(address: string): string =
proc getOpenseaAssets*(address: string, collectionSlug: string, limit: int): string =
let payload = %* [address, collectionSlug, limit]
result = callPrivateRPC("wallet_getOpenseaAssetsByOwnerAndCollection", payload)
result = callPrivateRPC("wallet_getOpenseaAssetsByOwnerAndCollection", payload)
proc fetchCryptoServices*(success: var bool): string =
success = true
try:
result = callPrivateRPC("wallet_getCryptoOnRamps")
except Exception as e:
success = false
error "Error getting crypto services: ", msg = e.msg
result = ""

View File

@ -21,6 +21,8 @@ from web3/conversions import `$`
export account, collectibles
include wallet2/async_tasks
logScope:
topics = "status-wallet2"
@ -210,4 +212,23 @@ QtObject:
result = status_wallet.getOpenseaCollections(address)
proc getOpenseaAssets*(address: string, collectionSlug: string, limit: int): string =
result = status_wallet.getOpenseaAssets(address, collectionSlug, limit)
result = status_wallet.getOpenseaAssets(address, collectionSlug, limit)
proc asyncFetchCryptoServices*(self: StatusWalletController) =
## Asynchronous request for the list of services to buy/sell crypto.
let arg = QObjectTaskArg(
tptr: cast[ByteAddress](asyncGetCryptoServicesTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onAsyncFetchCryptoServices"
)
self.tasks.threadpool.start(arg)
proc onAsyncFetchCryptoServices*(self: StatusWalletController,
response: string) {.slot.} =
let responseArray = response.parseJson
if (responseArray.kind != JArray):
info "received crypto services is not a json array"
self.events.emit("cryptoServicesFetched", CryptoServicesArg())
return
self.events.emit("cryptoServicesFetched", CryptoServicesArg(services: responseArray))

View File

@ -0,0 +1,16 @@
include ../utils/json_utils
#################################################
# Async request for the list of services to buy/sell crypto
#################################################
const asyncGetCryptoServicesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[QObjectTaskArg](argEncoded)
var success: bool
let response = status_wallet.fetchCryptoServices(success)
var list: JsonNode
if(success):
list = response.parseJson()["result"]
arg.finish($list)