fix: fix adding custom tokens

Fixes #4040 for real this time
This commit is contained in:
Jonathan Rainville 2021-11-09 14:40:09 -05:00 committed by Iuri Matias
parent 0c1dc30e5a
commit 2d0c95feb3
12 changed files with 202 additions and 123 deletions

View File

@ -124,11 +124,12 @@ proc newAppController*(appService: AppService): AppController =
# Services # Services
result.keychainService = keychain_service.newService(appService.status.events) result.keychainService = keychain_service.newService(appService.status.events)
result.settingService = setting_service.newService() result.settingService = setting_service.newService()
result.settingsService = settings_service.newService()
result.accountsService = accounts_service.newService() result.accountsService = accounts_service.newService()
result.contactsService = contacts_service.newService(appService.status.events, appService.threadpool) result.contactsService = contacts_service.newService(appService.status.events, appService.threadpool)
result.chatService = chat_service.newService() result.chatService = chat_service.newService()
result.communityService = community_service.newService(result.chatService) result.communityService = community_service.newService(result.chatService)
result.tokenService = token_service.newService(appService.status.events, result.settingService) result.tokenService = token_service.newService(appService.status.events, appService.threadpool, result.settingService, result.settingsService)
result.collectibleService = collectible_service.newService(result.settingService) result.collectibleService = collectible_service.newService(result.settingService)
result.walletAccountService = wallet_account_service.newService( result.walletAccountService = wallet_account_service.newService(
appService.status.events, result.settingService, result.tokenService appService.status.events, result.settingService, result.tokenService
@ -136,7 +137,6 @@ proc newAppController*(appService: AppService): AppController =
result.transactionService = transaction_service.newService(appService.status.events, appService.threadpool, result.walletAccountService) result.transactionService = transaction_service.newService(appService.status.events, appService.threadpool, result.walletAccountService)
result.bookmarkService = bookmark_service.newService() result.bookmarkService = bookmark_service.newService()
result.profileService = profile_service.newService() result.profileService = profile_service.newService()
result.settingsService = settings_service.newService()
result.aboutService = about_service.newService() result.aboutService = about_service.newService()
result.dappPermissionsService = dapp_permissions_service.newService() result.dappPermissionsService = dapp_permissions_service.newService()
result.languageService = language_service.newService() result.languageService = language_service.newService()

View File

@ -1,4 +1,6 @@
import ./controller_interface import ./controller_interface
import ./io_interface
import eventemitter
import ../../../../../app_service/service/token/service as token_service import ../../../../../app_service/service/token/service as token_service
import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../../app_service/service/wallet_account/service as wallet_account_service
@ -6,16 +8,19 @@ export controller_interface
type type
Controller*[T: controller_interface.DelegateInterface] = ref object of controller_interface.AccessInterface Controller*[T: controller_interface.DelegateInterface] = ref object of controller_interface.AccessInterface
delegate: T delegate: io_interface.AccessInterface
tokenService: token_service.ServiceInterface events: EventEmitter
tokenService: token_service.Service
walletAccountService: wallet_account_service.ServiceInterface walletAccountService: wallet_account_service.ServiceInterface
proc newController*[T]( proc newController*[T](
delegate: T, delegate: io_interface.AccessInterface,
tokenService: token_service.ServiceInterface, events: EventEmitter,
tokenService: token_service.Service,
walletAccountService: wallet_account_service.ServiceInterface, walletAccountService: wallet_account_service.ServiceInterface,
): Controller[T] = ): Controller[T] =
result = Controller[T]() result = Controller[T]()
result.events = events
result.delegate = delegate result.delegate = delegate
result.tokenService = tokenService result.tokenService = tokenService
result.walletAccountService = walletAccountService result.walletAccountService = walletAccountService
@ -24,7 +29,9 @@ method delete*[T](self: Controller[T]) =
discard discard
method init*[T](self: Controller[T]) = method init*[T](self: Controller[T]) =
discard self.events.on(SIGNAL_TOKEN_DETAILS_LOADED) do(e:Args):
let args = TokenDetailsLoadedArgs(e)
self.delegate.tokenDetailsWereResolved(args.tokenDetails)
method getTokens*[T](self: Controller[T]): seq[token_service.TokenDto] = method getTokens*[T](self: Controller[T]): seq[token_service.TokenDto] =
return self.tokenService.getTokens() return self.tokenService.getTokens()
@ -36,4 +43,7 @@ method toggleVisible*[T](self: Controller[T], symbol: string) =
self.walletAccountService.toggleTokenVisible(symbol) self.walletAccountService.toggleTokenVisible(symbol)
method removeCustomToken*[T](self: Controller[T], address: string) = method removeCustomToken*[T](self: Controller[T], address: string) =
self.tokenService.removeCustomToken(address) self.tokenService.removeCustomToken(address)
method getTokenDetails*[T](self: Controller[T], address: string) =
self.tokenService.getTokenDetails(address)

View File

@ -1,4 +1,4 @@
import ../../../../../app_service/service/token/service_interface as token_service import ../../../../../app_service/service/token/service as token_service
type type
AccessInterface* {.pure inheritable.} = ref object of RootObj AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -22,6 +22,9 @@ method toggleVisible*(self: AccessInterface, symbol: string) =
method removeCustomToken*(self: AccessInterface, address: string) = method removeCustomToken*(self: AccessInterface, address: string) =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getTokenDetails*(self: AccessInterface, address: string) =
raise newException(ValueError, "No implementation available")
type type
## Abstract class (concept) which must be implemented by object/s used in this ## Abstract class (concept) which must be implemented by object/s used in this
## module. ## module.

View File

@ -23,6 +23,12 @@ method removeCustomToken*(self: AccessInterface, address: string) {.base.} =
method refreshTokens*(self: AccessInterface) {.base.} = method refreshTokens*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getTokenDetails*(self: AccessInterface, address: string) {.base.} =
raise newException(ValueError, "No implementation available")
method tokenDetailsWereResolved*(self: AccessInterface, tokenDetails: string) {.base.} =
raise newException(ValueError, "No implementation available")
type type
## Abstract class (concept) which must be implemented by object/s used in this ## Abstract class (concept) which must be implemented by object/s used in this
## module. ## module.

View File

@ -20,14 +20,14 @@ type
proc newModule*[T]( proc newModule*[T](
delegate: T, delegate: T,
events: EventEmitter, events: EventEmitter,
tokenService: token_service.ServiceInterface, tokenService: token_service.Service,
walletAccountService: wallet_account_service.ServiceInterface, walletAccountService: wallet_account_service.ServiceInterface,
): Module[T] = ): Module[T] =
result = Module[T]() result = Module[T]()
result.delegate = delegate result.delegate = delegate
result.events = events result.events = events
result.view = newView(result) result.view = newView(result)
result.controller = controller.newController[Module[T]](result, tokenService, walletAccountService) result.controller = controller.newController[Module[T]](result, events, tokenService, walletAccountService)
result.moduleLoaded = false result.moduleLoaded = false
method delete*[T](self: Module[T]) = method delete*[T](self: Module[T]) =
@ -49,6 +49,7 @@ method refreshTokens*[T](self: Module[T]) =
) )
method load*[T](self: Module[T]) = method load*[T](self: Module[T]) =
self.controller.init()
singletonInstance.engine.setRootContextProperty("walletSectionAllTokens", newQVariant(self.view)) singletonInstance.engine.setRootContextProperty("walletSectionAllTokens", newQVariant(self.view))
self.refreshTokens() self.refreshTokens()
@ -73,4 +74,10 @@ method toggleVisible*[T](self: Module[T], symbol: string) =
self.controller.toggleVisible(symbol) self.controller.toggleVisible(symbol)
method removeCustomToken*[T](self: Module[T], address: string) = method removeCustomToken*[T](self: Module[T], address: string) =
self.controller.removeCustomToken(address) self.controller.removeCustomToken(address)
method getTokenDetails*[T](self: Module[T], address: string) =
self.controller.getTokenDetails(address)
method tokenDetailsWereResolved*[T](self: Module[T], tokenDetails: string) =
self.view.tokenDetailsWereResolved(tokenDetails)

View File

@ -65,4 +65,9 @@ QtObject:
self.delegate.toggleVisible(symbol) self.delegate.toggleVisible(symbol)
proc removeCustomToken(self: View, address: string) {.slot.} = proc removeCustomToken(self: View, address: string) {.slot.} =
self.delegate.removeCustomToken(address) self.delegate.removeCustomToken(address)
proc tokenDetailsWereResolved*(self: View, tokenDetails: string) {.signal.}
proc getTokenDetails*(self: View, address: string) {.slot.} =
self.delegate.getTokenDetails(address)

View File

@ -40,7 +40,7 @@ type
proc newModule*[T]( proc newModule*[T](
delegate: T, delegate: T,
events: EventEmitter, events: EventEmitter,
tokenService: token_service.ServiceInterface, tokenService: token_service.Service,
transactionService: transaction_service.Service, transactionService: transaction_service.Service,
collectibleService: collectible_service.ServiceInterface, collectibleService: collectible_service.ServiceInterface,
walletAccountService: wallet_account_service.ServiceInterface, walletAccountService: wallet_account_service.ServiceInterface,

View File

@ -0,0 +1,34 @@
# include strformat, json
include ../../common/json_utils
include ../../tasks/common
import status/[utils, tokens]
#################################################
# Async load transactions
#################################################
type
GetTokenDetailsTaskArg = ref object of QObjectTaskArg
chainId: int
address: string
const getTokenDetailsTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[GetTokenDetailsTaskArg](argEncoded)
try:
let
tkn = newErc20Contract(arg.chainId, arg.address.parseAddress)
decimals = tkn.tokenDecimals()
output = %* {
"address": arg.address,
"name": tkn.tokenName(),
"symbol": tkn.tokenSymbol(),
"decimals": (if decimals == 0: "" else: $decimals)
}
arg.finish(output)
except Exception as e:
let output = %* {
"address": arg.address,
"error": fmt"{e.msg}. Is this an ERC-20 or ERC-721 contract?",
}
arg.finish(output)

View File

@ -1,18 +1,30 @@
import json, sequtils, chronicles import NimQml, json, sequtils, chronicles, strformat, strutils
import eventemitter import eventemitter
from sugar import `=>` from sugar import `=>`
import web3/ethtypes import web3/ethtypes
from web3/conversions import `$` from web3/conversions import `$`
import status/statusgo_backend_new/custom_tokens as custom_tokens import status/statusgo_backend_new/custom_tokens as custom_tokens
import ../setting/service as setting_service import ../setting/service as setting_service
import ../settings/service as settings_service
import ./service_interface, ./dto, ./static_token import ./dto, ./static_token
export service_interface export dto
logScope: logScope:
topics = "token-service" topics = "token-service"
import ../../../app_service/[main]
import ../../../app_service/tasks/[qt, threadpool]
include async_tasks
# Signals which may be emitted by this service:
const SIGNAL_TOKEN_DETAILS_LOADED* = "SIGNAL_TOKEN_DETAILS_LOADED"
type
TokenDetailsLoadedArgs* = ref object of Args
tokenDetails*: string
type type
CustomTokenAdded* = ref object of Args CustomTokenAdded* = ref object of Args
@ -26,96 +38,122 @@ type
VisibilityToggled* = ref object of Args VisibilityToggled* = ref object of Args
token*: TokenDto token*: TokenDto
type QtObject:
Service* = ref object of service_interface.ServiceInterface type Service* = ref object of QObject
events: EventEmitter events: EventEmitter
threadpool: ThreadPool
settingService: setting_service.Service settingService: setting_service.Service
settingsService: settings_service.Service
tokens: seq[TokenDto] tokens: seq[TokenDto]
method delete*(self: Service) = proc delete*(self: Service) =
discard self.QObject.delete
proc newService*(events: EventEmitter, settingService: setting_service.Service): Service = proc newService*(
result = Service() events: EventEmitter,
result.events = events threadpool: ThreadPool,
result.settingService = settingService settingService: setting_service.Service,
result.tokens = @[] settingsService: settings_service.Service
): Service =
new(result, delete)
result.QObject.setup
result.events = events
result.threadpool = threadpool
result.settingService = settingService
result.settingsService = settingsService
result.tokens = @[]
method getDefaultVisibleSymbols(self: Service): seq[string] = proc getDefaultVisibleSymbols(self: Service): seq[string] =
let networkSlug = self.settingService.getSetting().currentNetwork.slug let networkSlug = self.settingService.getSetting().currentNetwork.slug
if networkSlug == "mainnet_rpc": if networkSlug == "mainnet_rpc":
return @["SNT"] return @["SNT"]
if networkSlug == "testnet_rpc" or networkSlug == "rinkeby_rpc": if networkSlug == "testnet_rpc" or networkSlug == "rinkeby_rpc":
return @["STT"] return @["STT"]
return @[] return @[]
method init*(self: Service) = proc init*(self: Service) =
try: try:
var activeTokenSymbols = self.settingService.getSetting().activeTokenSymbols var activeTokenSymbols = self.settingService.getSetting().activeTokenSymbols
if activeTokenSymbols.len == 0: if activeTokenSymbols.len == 0:
activeTokenSymbols = self.getDefaultVisibleSymbols() activeTokenSymbols = self.getDefaultVisibleSymbols()
let static_tokens = static_token.all().map( let static_tokens = static_token.all().map(
proc(x: TokenDto): TokenDto = proc(x: TokenDto): TokenDto =
x.isVisible = activeTokenSymbols.contains(x.symbol) x.isVisible = activeTokenSymbols.contains(x.symbol)
return x return x
)
let response = custom_tokens.getCustomTokens()
self.tokens = concat(
static_tokens,
map(response.result.getElems(), proc(x: JsonNode): TokenDto = x.toTokenDto(activeTokenSymbols))
).filter(
proc(x: TokenDto): bool = x.chainId == self.settingService.getSetting().currentNetwork.id
)
except Exception as e:
let errDesription = e.msg
error "error: ", errDesription
return
proc getTokens*(self: Service): seq[TokenDto] =
return self.tokens
proc addCustomToken*(self: Service, address: string, name: string, symbol: string, decimals: int) =
custom_tokens.addCustomToken(address, name, symbol, decimals, "")
let token = newDto(
name,
self.settingService.getSetting().currentNetwork.id,
fromHex(Address, address),
symbol,
decimals,
false,
true
) )
self.tokens.add(token)
self.events.emit("token/customTokenAdded", CustomTokenAdded(token: token))
let response = custom_tokens.getCustomTokens() proc toggleVisible*(self: Service, symbol: string) =
self.tokens = concat( var tokenChanged = self.tokens[0]
static_tokens, for token in self.tokens:
map(response.result.getElems(), proc(x: JsonNode): TokenDto = x.toTokenDto(activeTokenSymbols)) if token.symbol == symbol:
).filter( token.isVisible = not token.isVisible
proc(x: TokenDto): bool = x.chainId == self.settingService.getSetting().currentNetwork.id tokenChanged = token
break
let visibleSymbols = self.tokens.filter(t => t.isVisible).map(t => t.symbol)
discard self.settingService.saveSetting("wallet/visible-tokens", visibleSymbols)
self.events.emit("token/visibilityToggled", VisibilityToggled(token: tokenChanged))
proc removeCustomToken*(self: Service, address: string) =
custom_tokens.removeCustomToken(address)
var index = -1
for idx, token in self.tokens.pairs():
if $token.address == address:
index = idx
break
let tokenRemoved = self.tokens[index]
self.tokens.del(index)
self.events.emit("token/customTokenRemoved", CustomTokenRemoved(token: tokenRemoved))
proc tokenDetailsResolved*(self: Service, tokenDetails: string) {.slot.} =
self.events.emit(SIGNAL_TOKEN_DETAILS_LOADED, TokenDetailsLoadedArgs(
tokenDetails: tokenDetails
))
proc getTokenDetails*(self: Service, address: string) =
let chainId = self.settingsService.getCurrentNetworkDetails().config.networkId
let arg = GetTokenDetailsTaskArg(
tptr: cast[ByteAddress](getTokenDetailsTask),
vptr: cast[ByteAddress](self.vptr),
slot: "tokenDetailsResolved",
chainId: chainId,
address: address
) )
self.threadpool.start(arg)
except Exception as e:
let errDesription = e.msg
error "error: ", errDesription
return
method getTokens*(self: Service): seq[TokenDto] =
return self.tokens
method addCustomToken*(self: Service, address: string, name: string, symbol: string, decimals: int) =
custom_tokens.addCustomToken(address, name, symbol, decimals, "")
let token = newDto(
name,
self.settingService.getSetting().currentNetwork.id,
fromHex(Address, address),
symbol,
decimals,
false,
true
)
self.tokens.add(token)
self.events.emit("token/customTokenAdded", CustomTokenAdded(token: token))
method toggleVisible*(self: Service, symbol: string) =
var tokenChanged = self.tokens[0]
for token in self.tokens:
if token.symbol == symbol:
token.isVisible = not token.isVisible
tokenChanged = token
break
let visibleSymbols = self.tokens.filter(t => t.isVisible).map(t => t.symbol)
discard self.settingService.saveSetting("wallet/visible-tokens", visibleSymbols)
self.events.emit("token/visibilityToggled", VisibilityToggled(token: tokenChanged))
method removeCustomToken*(self: Service, address: string) =
custom_tokens.removeCustomToken(address)
var index = -1
for idx, token in self.tokens.pairs():
if $token.address == address:
index = idx
break
let tokenRemoved = self.tokens[index]
self.tokens.del(index)
self.events.emit("token/customTokenRemoved", CustomTokenRemoved(token: tokenRemoved))

View File

@ -1,25 +0,0 @@
import dto
export dto
type
ServiceInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for this service access.
method delete*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method init*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getTokens*(self: ServiceInterface): seq[TokenDto] {.base.} =
raise newException(ValueError, "No implementation available")
method addCustomToken*(self: ServiceInterface, address: string, name: string, symbol: string, decimals: int) =
raise newException(ValueError, "No implementation available")
method toggleVisible*(self: ServiceInterface, symbol: string) =
raise newException(ValueError, "No implementation available")
method removeCustomToken*(self: ServiceInterface, address: string) =
raise newException(ValueError, "No implementation available")

View File

@ -57,7 +57,7 @@ ModalPopup {
} }
property var getTokenDetails: Backpressure.debounce(popup, 500, function (tokenAddress){ property var getTokenDetails: Backpressure.debounce(popup, 500, function (tokenAddress){
RootStore.customTokenList.getTokenDetails(tokenAddress) RootStore.walletTokensModule.getTokenDetails(tokenAddress)
}); });
function onKeyReleased(){ function onKeyReleased(){
@ -70,7 +70,7 @@ ModalPopup {
Item { Item {
Connections { Connections {
target: RootStore.customTokenList target: RootStore.walletTokensModule
onTokenDetailsWereResolved: { onTokenDetailsWereResolved: {
const jsonObj = JSON.parse(tokenDetails) const jsonObj = JSON.parse(tokenDetails)
if (jsonObj.error) { if (jsonObj.error) {

View File

@ -17,6 +17,7 @@ QtObject {
property string signingPhrase: walletSection.signingPhrase property string signingPhrase: walletSection.signingPhrase
property string mnemonicBackedUp: walletSection.isMnemonicBackedUp property string mnemonicBackedUp: walletSection.isMnemonicBackedUp
property var walletTokensModule: walletSectionAllTokens
property var defaultTokenList: walletSectionAllTokens.default property var defaultTokenList: walletSectionAllTokens.default
property var customTokenList: walletSectionAllTokens.custom property var customTokenList: walletSectionAllTokens.custom
property var tokens: walletSectionAllTokens.all property var tokens: walletSectionAllTokens.all