mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-03 18:25:33 +00:00
feat(@desktop/stickers): support buying stickers if profile keypair is migrated to a keycard
Closes part 1 of #12556
This commit is contained in:
parent
82a829de0c
commit
a057e93901
@ -220,7 +220,8 @@ proc newModule*[T](
|
||||
devicesService, mailserversService, chatService, ensService, walletAccountService, generalService, communityService,
|
||||
networkService, keycardService, keychainService, tokenService
|
||||
)
|
||||
result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService, networkService, tokenService)
|
||||
result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService,
|
||||
networkService, tokenService, keycardService)
|
||||
result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService,
|
||||
messageService, chatService, communityService)
|
||||
result.communitiesModule = communities_module.newModule(result, events, communityService, contactsService, communityTokensService,
|
||||
|
@ -1,16 +1,18 @@
|
||||
import Tables, stint, json
|
||||
import Tables, os, uuids, stint, json
|
||||
|
||||
import ./io_interface
|
||||
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/stickers/service as stickers_service
|
||||
import ../../../../app_service/service/token/service
|
||||
import ../../../../app_service/service/settings/service as settings_service
|
||||
import ../../../../app_service/service/network/service as network_service
|
||||
import ../../../../app_service/service/eth/utils as eth_utils
|
||||
import ../../../../app_service/service/wallet_account/service as wallet_account_service
|
||||
import ../../../../app_service/service/token/service as token_service
|
||||
import ../../shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||
import app/core/eventemitter
|
||||
import app_service/service/node/service as node_service
|
||||
import app_service/service/stickers/service as stickers_service
|
||||
import app_service/service/token/service
|
||||
import app_service/service/settings/service as settings_service
|
||||
import app_service/service/network/service as network_service
|
||||
import app_service/service/eth/utils as eth_utils
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
import app_service/service/token/service as token_service
|
||||
import app_service/service/keycard/service as keycard_service
|
||||
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||
|
||||
const UNIQUE_BUY_STICKER_TRANSACTION_MODULE_IDENTIFIER* = "StickersSection-TransactionModule"
|
||||
|
||||
@ -23,6 +25,8 @@ type
|
||||
networkService: network_service.Service
|
||||
walletAccountService: wallet_account_service.Service
|
||||
tokenService: token_service.Service
|
||||
keycardService: keycard_service.Service
|
||||
connectionKeycardResponse: UUID
|
||||
disconnected: bool
|
||||
|
||||
proc newController*(
|
||||
@ -33,6 +37,7 @@ proc newController*(
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
networkService: network_service.Service,
|
||||
tokenService: token_service.Service,
|
||||
keycardService: keycard_service.Service
|
||||
): Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
@ -42,6 +47,7 @@ proc newController*(
|
||||
result.networkService = networkService
|
||||
result.walletAccountService = walletAccountService
|
||||
result.tokenService = tokenService
|
||||
result.keycardService = keycardService
|
||||
result.disconnected = false
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
@ -90,16 +96,25 @@ proc init*(self: Controller) =
|
||||
let args = SharedKeycarModuleArgs(e)
|
||||
if args.uniqueIdentifier != UNIQUE_BUY_STICKER_TRANSACTION_MODULE_IDENTIFIER:
|
||||
return
|
||||
self.delegate.onUserAuthenticated(args.password)
|
||||
self.delegate.onKeypairAuthenticated(args.password, args.pin)
|
||||
|
||||
self.events.on(SIGNAL_STICKER_PACK_INSTALLED) do(e: Args):
|
||||
let args = StickerPackInstalledArgs(e)
|
||||
self.delegate.onStickerPackInstalled(args.packId)
|
||||
|
||||
proc buy*(self: Controller, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): StickerBuyResultArgs =
|
||||
self.stickerService.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
|
||||
proc prepareTxForBuyingStickers*(self: Controller, chainId: int, packId: string, address: string, gas: string, gasPrice: string,
|
||||
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool): JsonNode =
|
||||
return self.stickerService.prepareTxForBuyingStickers(chainId, packId, address, gas, gasPrice, maxPriorityFeePerGas,
|
||||
maxFeePerGas, eip1559Enabled)
|
||||
|
||||
proc getRecentStickers*(self: Controller): seq[StickerDto] =
|
||||
proc signBuyingStickersTxLocally*(self: Controller, data, account, hashedPasssword: string): string =
|
||||
return self.stickerService.signBuyingStickersTxLocally(data, account, hashedPasssword)
|
||||
|
||||
proc sendBuyingStickersTxWithSignatureAndWatch*(self: Controller, chainId: int, txData: JsonNode, packId: string,
|
||||
signature: string): StickerBuyResultArgs =
|
||||
return self.stickerService.sendBuyingStickersTxWithSignatureAndWatch(chainId, txData, packId, signature)
|
||||
|
||||
proc getRecentStickers*(self: Controller): seq[StickerDto] =
|
||||
return self.stickerService.getRecentStickers()
|
||||
|
||||
proc loadRecentStickers*(self: Controller) =
|
||||
@ -152,6 +167,9 @@ proc getSNTBalance*(self: Controller): string =
|
||||
proc getWalletDefaultAddress*(self: Controller): string =
|
||||
return self.walletAccountService.getWalletAccount(0).address
|
||||
|
||||
proc getKeypairByAccountAddress*(self: Controller, address: string): KeypairDto =
|
||||
return self.walletAccountService.getKeypairByAccountAddress(address)
|
||||
|
||||
proc getCurrentCurrency*(self: Controller): string =
|
||||
return self.settingsService.getCurrency()
|
||||
|
||||
@ -174,7 +192,31 @@ proc getStatusToken*(self: Controller): string =
|
||||
}
|
||||
return $jsonObj
|
||||
|
||||
proc authenticateUser*(self: Controller, keyUid = "") =
|
||||
proc authenticate*(self: Controller, keyUid = "") =
|
||||
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_BUY_STICKER_TRANSACTION_MODULE_IDENTIFIER,
|
||||
keyUid: keyUid)
|
||||
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
|
||||
|
||||
proc disconnectKeycardReponseSignal(self: Controller) =
|
||||
self.events.disconnect(self.connectionKeycardResponse)
|
||||
|
||||
proc connectKeycardReponseSignal(self: Controller) =
|
||||
self.connectionKeycardResponse = self.events.onWithUUID(SIGNAL_KEYCARD_RESPONSE) do(e: Args):
|
||||
let args = KeycardLibArgs(e)
|
||||
self.disconnectKeycardReponseSignal()
|
||||
let currentFlow = self.keycardService.getCurrentFlow()
|
||||
if currentFlow != KCSFlowType.Sign:
|
||||
self.delegate.onTransactionSigned("", KeycardEvent())
|
||||
return
|
||||
self.delegate.onTransactionSigned(args.flowType, args.flowEvent)
|
||||
|
||||
proc cancelCurrentFlow*(self: Controller) =
|
||||
self.keycardService.cancelCurrentFlow()
|
||||
# in most cases we're running another flow after canceling the current one,
|
||||
# this way we're giving to the keycard some time to cancel the current flow
|
||||
sleep(200)
|
||||
|
||||
proc runSignFlow*(self: Controller, pin, bip44Path, txHash: string) =
|
||||
self.cancelCurrentFlow()
|
||||
self.connectKeycardReponseSignal()
|
||||
self.keycardService.startSignFlow(bip44Path, txHash, pin)
|
@ -1,6 +1,9 @@
|
||||
import Tables, stint
|
||||
import ./item
|
||||
import ../../../../app_service/service/stickers/service as stickers_service
|
||||
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
import app_service/service/stickers/service as stickers_service
|
||||
from app_service/service/keycard/service import KeycardEvent
|
||||
|
||||
type
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
@ -109,5 +112,8 @@ method stickerTransactionConfirmed*(self: AccessInterface, trxType: string, pack
|
||||
method stickerTransactionReverted*(self: AccessInterface, trxType: string, packID: string, transactionHash: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
|
||||
method onKeypairAuthenticated*(self: AccessInterface, password: string, pin: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onTransactionSigned*(self: AccessInterface, keycardFlowType: string, keycardEvent: KeycardEvent) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
@ -1,14 +1,18 @@
|
||||
import NimQml, Tables, stint, sugar, sequtils, json, strutils, strformat, parseutils
|
||||
import NimQml, Tables, stint, sugar, sequtils, json, strutils, strformat, parseutils, chronicles
|
||||
import ./io_interface, ./view, ./controller, ./item, ./models/sticker_pack_list
|
||||
import ../io_interface as delegate_interface
|
||||
import ../../../global/global_singleton
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/stickers/service as stickers_service
|
||||
import ../../../../app_service/service/settings/service as settings_service
|
||||
import ../../../../app_service/service/network/service as network_service
|
||||
import ../../../../app_service/common/conversion as service_conversion
|
||||
import ../../../../app_service/service/wallet_account/service as wallet_account_service
|
||||
import ../../../../app_service/service/token/service as token_service
|
||||
import app/global/global_singleton
|
||||
import app/core/eventemitter
|
||||
import app_service/service/stickers/service as stickers_service
|
||||
import app_service/service/settings/service as settings_service
|
||||
import app_service/service/network/service as network_service
|
||||
import app_service/common/conversion as service_conversion
|
||||
import app_service/common/utils as common_utils
|
||||
import app_service/common/wallet_constants as common_wallet_constants
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
import app_service/service/token/service as token_service
|
||||
import app_service/service/keycard/service as keycard_service
|
||||
import app_service/service/keycard/constants as keycard_constants
|
||||
|
||||
export io_interface
|
||||
|
||||
@ -18,12 +22,13 @@ const cancelledRequest* = "cancelled"
|
||||
type TmpBuyStickersTransactionDetails = object
|
||||
packId: string
|
||||
address: string
|
||||
addressPath: string
|
||||
gas: string
|
||||
gasPrice: string
|
||||
maxPriorityFeePerGas: string
|
||||
maxFeePerGas: string
|
||||
eip1559Enabled: bool
|
||||
|
||||
txData: JsonNode
|
||||
|
||||
type
|
||||
Module* = ref object of io_interface.AccessInterface
|
||||
@ -42,12 +47,14 @@ proc newModule*(
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
networkService: network_service.Service,
|
||||
tokenService: token_service.Service,
|
||||
keycardService: keycard_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.view = newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, stickersService, settingsService, walletAccountService, networkService, tokenService)
|
||||
result.controller = controller.newController(result, events, stickersService, settingsService, walletAccountService,
|
||||
networkService, tokenService, keycardService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
singletonInstance.engine.setRootContextProperty("stickersModule", result.viewVariant)
|
||||
@ -55,6 +62,13 @@ proc newModule*(
|
||||
method delete*(self: Module) =
|
||||
self.view.delete
|
||||
|
||||
proc clear(self: Module) =
|
||||
self.tmpBuyStickersTransactionDetails = TmpBuyStickersTransactionDetails()
|
||||
|
||||
proc finish(self: Module, chainId: int, txHash: string, error: string) =
|
||||
self.clear()
|
||||
self.view.transactionWasSent(chainId, txHash, error)
|
||||
|
||||
method load*(self: Module) =
|
||||
self.controller.init()
|
||||
let signingPhrase = self.controller.getSigningPhrase()
|
||||
@ -76,53 +90,100 @@ method authenticateAndBuy*(self: Module, packId: string, address: string, gas: s
|
||||
self.tmpBuyStickersTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
|
||||
self.tmpBuyStickersTransactionDetails.maxFeePerGas = maxFeePerGas
|
||||
self.tmpBuyStickersTransactionDetails.eip1559Enabled = eip1559Enabled
|
||||
self.tmpBuyStickersTransactionDetails.txData = nil
|
||||
|
||||
if singletonInstance.userProfile.getIsKeycardUser():
|
||||
let keyUid = singletonInstance.userProfile.getKeyUid()
|
||||
self.controller.authenticateUser(keyUid)
|
||||
let kp = self.controller.getKeypairByAccountAddress(address)
|
||||
if kp.migratedToKeycard():
|
||||
let accounts = kp.accounts.filter(acc => cmpIgnoreCase(acc.address, address) == 0)
|
||||
if accounts.len != 1:
|
||||
error "cannot resolve selected account to send from among known keypair accounts"
|
||||
return
|
||||
self.tmpBuyStickersTransactionDetails.addressPath = accounts[0].path
|
||||
self.controller.authenticate(kp.keyUid)
|
||||
else:
|
||||
self.controller.authenticateUser()
|
||||
self.controller.authenticate()
|
||||
|
||||
##################################
|
||||
## Do Not Delete
|
||||
##
|
||||
## Once we start with signing a transactions we shold check if the address we want to send a transaction from is migrated
|
||||
## or not. In case it's not we should just authenticate logged in user, otherwise we should use one of the keycards that
|
||||
## address (key pair) is migrated to and sign the transaction using it.
|
||||
##
|
||||
## The code bellow is an example how we can achieve that in future, when we start with signing transactions.
|
||||
##
|
||||
## let acc = self.controller.getAccountByAddress(from_addr)
|
||||
## if acc.isNil:
|
||||
## echo "error: selected account to send a transaction from is not known"
|
||||
## return
|
||||
## let keyPair = self.controller.getKeycardsWithSameKeyUid(acc.keyUid)
|
||||
## if keyPair.len == 0:
|
||||
## self.controller.authenticateUser()
|
||||
## else:
|
||||
## self.controller.authenticateUser(acc.keyUid, acc.path)
|
||||
##
|
||||
##################################
|
||||
proc sendBuyingStickersTxWithSignatureAndWatch(self: Module, signature: string) =
|
||||
if self.tmpBuyStickersTransactionDetails.txData.isNil:
|
||||
let errMsg = "unexpected error while sending buying stickers tx"
|
||||
error "error", msg=errMsg, methodName="sendBuyingStickersTxWithSignatureAndWatch"
|
||||
self.finish(chainId = 0, txHash = "", error = errMsg)
|
||||
return
|
||||
|
||||
method onUserAuthenticated*(self: Module, password: string) =
|
||||
let response = self.controller.sendBuyingStickersTxWithSignatureAndWatch(
|
||||
self.getChainIdForStickers(),
|
||||
self.tmpBuyStickersTransactionDetails.txData,
|
||||
self.tmpBuyStickersTransactionDetails.packId,
|
||||
signature
|
||||
)
|
||||
|
||||
if not response.error.isEmptyOrWhitespace():
|
||||
error "sending buying stickers tx failed", errMsg=response.error, methodName="sendBuyingStickersTxWithSignatureAndWatch"
|
||||
self.finish(chainId = 0, txHash = "", error = response.error)
|
||||
return
|
||||
|
||||
self.view.stickerPacks.updateStickerPackInList(self.tmpBuyStickersTransactionDetails.packId, installed = false, pending = true)
|
||||
self.finish(response.chainId, response.txHash, response.error)
|
||||
|
||||
method onKeypairAuthenticated*(self: Module, password: string, pin: string) =
|
||||
if password.len == 0:
|
||||
let response = %* {"success": false, "error": cancelledRequest}
|
||||
self.view.transactionWasSent(chainId = 0, txHash = "", error = cancelledRequest)
|
||||
else:
|
||||
let response = self.controller.buy(
|
||||
self.tmpBuyStickersTransactionDetails.packId,
|
||||
self.tmpBuyStickersTransactionDetails.address,
|
||||
self.tmpBuyStickersTransactionDetails.gas,
|
||||
self.tmpBuyStickersTransactionDetails.gasPrice,
|
||||
self.tmpBuyStickersTransactionDetails.maxPriorityFeePerGas,
|
||||
self.tmpBuyStickersTransactionDetails.maxFeePerGas,
|
||||
password,
|
||||
self.tmpBuyStickersTransactionDetails.eip1559Enabled
|
||||
)
|
||||
if response.error.isEmptyOrWhitespace():
|
||||
self.view.stickerPacks.updateStickerPackInList(self.tmpBuyStickersTransactionDetails.packId, installed = false,
|
||||
pending = true)
|
||||
self.view.transactionWasSent(chainId = response.chainId, txHash = response.txHash, error = response.error)
|
||||
self.finish(chainId = 0, txHash = "", error = cancelledRequest)
|
||||
return
|
||||
|
||||
let chainId = self.getChainIdForStickers()
|
||||
let txDataJson = self.controller.prepareTxForBuyingStickers(
|
||||
chainId,
|
||||
self.tmpBuyStickersTransactionDetails.packId,
|
||||
self.tmpBuyStickersTransactionDetails.address,
|
||||
self.tmpBuyStickersTransactionDetails.gas,
|
||||
self.tmpBuyStickersTransactionDetails.gasPrice,
|
||||
self.tmpBuyStickersTransactionDetails.maxPriorityFeePerGas,
|
||||
self.tmpBuyStickersTransactionDetails.maxFeePerGas,
|
||||
self.tmpBuyStickersTransactionDetails.eip1559Enabled
|
||||
)
|
||||
|
||||
if txDataJson.isNil or
|
||||
txDataJson.kind != JsonNodeKind.JObject or
|
||||
not txDataJson.hasKey("txArgs") or
|
||||
not txDataJson.hasKey("messageToSign"):
|
||||
let errMsg = "unexpected response format preparing tx for buying stickers"
|
||||
error "error", msg=errMsg, methodName="onKeypairAuthenticated"
|
||||
self.finish(chainId = 0, txHash = "", error = errMsg)
|
||||
return
|
||||
|
||||
var txToBeSigned = txDataJson["messageToSign"].getStr
|
||||
if txToBeSigned.len != common_wallet_constants.TX_HASH_LEN_WITH_PREFIX:
|
||||
let errMsg = "unexpected tx hash length"
|
||||
error "error", msg=errMsg, methodName="onKeypairAuthenticated"
|
||||
self.finish(chainId = 0, txHash = "", error = errMsg)
|
||||
return
|
||||
|
||||
self.tmpBuyStickersTransactionDetails.txData = txDataJson["txArgs"]
|
||||
|
||||
if txDataJson.hasKey("signOnKeycard") and txDataJson["signOnKeycard"].getBool:
|
||||
if pin.len != PINLengthForStatusApp:
|
||||
let errMsg = "cannot proceed with keycard signing, unexpected pin"
|
||||
error "error", msg=errMsg, methodName="onKeypairAuthenticated"
|
||||
self.finish(chainId = 0, txHash = "", error = errMsg)
|
||||
return
|
||||
var txForKcFlow = txToBeSigned
|
||||
if txForKcFlow.startsWith("0x"):
|
||||
txForKcFlow = txForKcFlow[2..^1]
|
||||
self.controller.runSignFlow(pin, self.tmpBuyStickersTransactionDetails.addressPath, txForKcFlow)
|
||||
return
|
||||
|
||||
var finalPassword = password
|
||||
if pin.len == 0:
|
||||
finalPassword = common_utils.hashPassword(password)
|
||||
|
||||
let signature = self.controller.signBuyingStickersTxLocally(txToBeSigned, self.tmpBuyStickersTransactionDetails.address, finalPassword)
|
||||
if signature.len == 0:
|
||||
let errMsg = "couldn't sign tx locally"
|
||||
error "error", msg=errMsg, methodName="onKeypairAuthenticated"
|
||||
self.finish(chainId = 0, txHash = "", error = errMsg)
|
||||
return
|
||||
|
||||
self.sendBuyingStickersTxWithSignatureAndWatch(signature)
|
||||
|
||||
method obtainMarketStickerPacks*(self: Module) =
|
||||
self.controller.obtainMarketStickerPacks()
|
||||
@ -258,3 +319,12 @@ method stickerTransactionConfirmed*(self: Module, trxType: string, packID: strin
|
||||
method stickerTransactionReverted*(self: Module, trxType: string, packID: string, transactionHash: string) =
|
||||
self.view.stickerPacks.updateStickerPackInList(packID, installed = false, pending = false)
|
||||
self.view.emitTransactionCompletedSignal(false, transactionHash, packID, trxType)
|
||||
|
||||
method onTransactionSigned*(self: Module, keycardFlowType: string, keycardEvent: KeycardEvent) =
|
||||
if keycardFlowType != keycard_constants.ResponseTypeValueKeycardFlowResult:
|
||||
let errMsg = "unexpected error while keycard signing transaction"
|
||||
error "error", msg=errMsg, methodName="onTransactionSigned"
|
||||
self.finish(chainId = 0, txHash = "", error = errMsg)
|
||||
return
|
||||
let signature = "0x" & keycardEvent.txSignature.r & keycardEvent.txSignature.s & keycardEvent.txSignature.v
|
||||
self.sendBuyingStickersTxWithSignatureAndWatch(signature)
|
@ -1,3 +1,7 @@
|
||||
const ETH_TRANSACTION_TYPE* = "eth"
|
||||
const ERC20_TRANSACTION_TYPE* = "erc20"
|
||||
const ERC721_TRANSACTION_TYPE* = "erc721"
|
||||
const
|
||||
ETH_TRANSACTION_TYPE* = "eth"
|
||||
ERC20_TRANSACTION_TYPE* = "erc20"
|
||||
ERC721_TRANSACTION_TYPE* = "erc721"
|
||||
|
||||
TX_HASH_LEN* = 32 * 2
|
||||
TX_HASH_LEN_WITH_PREFIX* = TX_HASH_LEN + 2
|
@ -13,6 +13,8 @@ import ../../../backend/chat as status_chat
|
||||
import ../../../backend/response_type
|
||||
import ../../../backend/eth as status_eth
|
||||
import ../../../backend/backend as status_go_backend
|
||||
import ../../../backend/wallet_connect as status_wallet_connect
|
||||
import ../../../backend/wallet as status_wallet
|
||||
import ./dto/stickers
|
||||
import ../ens/utils as ens_utils
|
||||
import ../token/service as token_service
|
||||
@ -44,7 +46,7 @@ type
|
||||
estimate*: int
|
||||
uuid*: string
|
||||
GasPriceArgs* = ref object of Args
|
||||
gasPrice*: string
|
||||
gasPrice*: string
|
||||
StickerTransactionArgs* = ref object of Args
|
||||
transactionHash*: string
|
||||
packID*: string
|
||||
@ -121,7 +123,7 @@ QtObject:
|
||||
|
||||
proc getInstalledStickerPacks*(self: Service): Table[string, StickerPackDto] =
|
||||
return self.installedStickerPacks
|
||||
|
||||
|
||||
proc getStickerMarketAddress*(self: Service): string =
|
||||
try:
|
||||
let chainId = self.networkService.getNetworkForStickers().chainId
|
||||
@ -133,7 +135,7 @@ QtObject:
|
||||
proc confirmTransaction(self: Service, trxType: string, packID: string, transactionHash: string) =
|
||||
try:
|
||||
if not self.marketStickerPacks.contains(packID):
|
||||
let pendingStickerPacksResponse = status_stickers.pending()
|
||||
let pendingStickerPacksResponse = status_stickers.pending()
|
||||
for (pID, stickerPackJson) in pendingStickerPacksResponse.result.pairs():
|
||||
if packID != pID: continue
|
||||
self.marketStickerPacks[packID] = stickerPackJson.toStickerPackDto()
|
||||
@ -155,7 +157,7 @@ QtObject:
|
||||
proc revertTransaction(self: Service, trxType: string, packID: string, transactionHash: string) =
|
||||
try:
|
||||
if not self.marketStickerPacks.contains(packID):
|
||||
let pendingStickerPacksResponse = status_stickers.pending()
|
||||
let pendingStickerPacksResponse = status_stickers.pending()
|
||||
for (pID, stickerPackJson) in pendingStickerPacksResponse.result.pairs():
|
||||
if packID != pID: continue
|
||||
self.marketStickerPacks[packID] = stickerPackJson.toStickerPackDto()
|
||||
@ -181,68 +183,10 @@ QtObject:
|
||||
else:
|
||||
self.revertTransaction($PendingTransactionTypeDto.BuyStickerPack, receivedData.data, receivedData.transactionHash)
|
||||
|
||||
proc buildTransaction*(
|
||||
source: Address,
|
||||
gas = "",
|
||||
gasPrice = "",
|
||||
isEIP1559Enabled = false,
|
||||
maxPriorityFeePerGas = "",
|
||||
maxFeePerGas = "",
|
||||
): TransactionDataDto =
|
||||
result = TransactionDataDto(
|
||||
source: source,
|
||||
value: (0.u256).some,
|
||||
gas: (if gas.isEmptyOrWhitespace: Quantity.none else: Quantity(cast[uint64](parseFloat(gas).toUInt64)).some)
|
||||
)
|
||||
if isEIP1559Enabled:
|
||||
result.maxPriorityFeePerGas = if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxPriorityFeePerGas)).some
|
||||
result.maxFeePerGas = (if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxFeePerGas)).some)
|
||||
else:
|
||||
result.gasPrice = (if gasPrice.isEmptyOrWhitespace: int.none else: gwei2Wei(parseFloat(gasPrice)).truncate(int).some)
|
||||
|
||||
proc getStatusToken*(self: Service): TokenDto =
|
||||
let networkDto = self.networkService.getNetworkForStickers()
|
||||
|
||||
return self.tokenService.findTokenBySymbol(networkDto.chainId, networkDto.sntSymbol())
|
||||
|
||||
proc buyPack*(self: Service, packId: string, address, gas, gasPrice: string, eip1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): tuple[txHash: string, error: string] =
|
||||
let
|
||||
chainId = self.networkService.getNetworkForStickers().chainId
|
||||
txData = buildTransaction(parseAddress(address), gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
|
||||
try:
|
||||
let transactionResponse = status_stickers.buy(chainId, %txData, packId, common_utils.hashPassword(password))
|
||||
let transactionHash = transactionResponse.result.getStr()
|
||||
let sntContract = self.getStatusToken()
|
||||
self.transactionService.watchTransaction(
|
||||
transactionHash,
|
||||
address,
|
||||
$sntContract.address,
|
||||
$PendingTransactionTypeDto.BuyStickerPack,
|
||||
packId,
|
||||
chainId,
|
||||
)
|
||||
return (txHash: transactionHash, error: "")
|
||||
except ValueError:
|
||||
let message = getCurrentExceptionMsg()
|
||||
var error = message
|
||||
if message.contains("could not decrypt key with given password"):
|
||||
error = "could not decrypt key with given password"
|
||||
error "Error sending transaction", message
|
||||
return (txHash: "", error: error)
|
||||
except RpcException:
|
||||
error "Error sending transaction", message = getCurrentExceptionMsg()
|
||||
|
||||
proc buy*(self: Service, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): StickerBuyResultArgs =
|
||||
try:
|
||||
status_utils.validateTransactionInput(address, address, "", "0", gas, gasPrice, "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, "ok")
|
||||
except Exception as e:
|
||||
error "Error buying sticker pack", msg = e.msg
|
||||
return StickerBuyResultArgs(chainId: 0, txHash: "", error: e.msg)
|
||||
|
||||
var (txHash, err) = self.buyPack(packId, address, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, password)
|
||||
|
||||
return StickerBuyResultArgs(chainId: self.networkService.getNetworkForStickers().chainId, txHash: txHash, error: err)
|
||||
|
||||
proc setMarketStickerPacks*(self: Service, strickersJSON: string) {.slot.} =
|
||||
let stickersResult = Json.decode(strickersJSON, tuple[packs: seq[StickerPackDto], error: string])
|
||||
|
||||
@ -263,8 +207,7 @@ QtObject:
|
||||
isPending: false
|
||||
))
|
||||
|
||||
# TODO move this to be async
|
||||
let pendingStickerPacksResponse = status_stickers.pending()
|
||||
let pendingStickerPacksResponse = status_stickers.pending()
|
||||
for (packID, stickerPackJson) in pendingStickerPacksResponse.result.pairs():
|
||||
if self.marketStickerPacks.contains(packID): continue
|
||||
self.marketStickerPacks[packID] = stickerPackJson.toStickerPackDto()
|
||||
@ -411,7 +354,7 @@ QtObject:
|
||||
))
|
||||
else:
|
||||
error "Sticker pack did not get installed", packId = installedPack.packId
|
||||
|
||||
|
||||
proc uninstallStickerPack*(self: Service, packId: string) =
|
||||
try:
|
||||
discard status_stickers.uninstall(packId)
|
||||
@ -453,3 +396,98 @@ QtObject:
|
||||
|
||||
let balances = status_go_backend.getTokensBalancesForChainIDs(@[network.chainId], @[account], @[token.address]).result
|
||||
return ens_utils.hex2Token(balances{account}{token.address}.getStr, token.decimals)
|
||||
|
||||
# proc prepareTxForBuyingStickers*(self: Service, chainId: int, packId: string, address: string): JsonNode =
|
||||
proc prepareTxForBuyingStickers*(self: Service, chainId: int, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
|
||||
maxFeePerGas: string, eip1559Enabled: bool): JsonNode =
|
||||
try:
|
||||
var prepareTxResponse = status_stickers.prepareTxForBuyingStickers(chainId, address, packId)
|
||||
if not prepareTxResponse.error.isNil:
|
||||
error "error occurred", procName="prepareTxForBuyingStickers", msg = prepareTxResponse.error.message
|
||||
return
|
||||
|
||||
prepareTxResponse.result["gas"] = %* (if gas.isEmptyOrWhitespace: Quantity.none else: Quantity(cast[uint64](parseFloat(gas).toUInt64)).some)
|
||||
if eip1559Enabled:
|
||||
let maxPriorityFeePerGasFinal = if maxPriorityFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxPriorityFeePerGas)).some
|
||||
let maxFeePerGasFinal = if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxFeePerGas)).some
|
||||
prepareTxResponse.result["maxPriorityFeePerGas"] = %* ("0x" & maxPriorityFeePerGasFinal.unsafeGet.toHex)
|
||||
prepareTxResponse.result["maxFeePerGas"] = %* ("0x" & maxFeePerGasFinal.unsafeGet.toHex)
|
||||
else:
|
||||
let gasPriceFinal = if gasPrice.isEmptyOrWhitespace: int.none else: gwei2Wei(parseFloat(gasPrice)).truncate(int).some
|
||||
prepareTxResponse.result["gasPrice"] = %* ("0x" & gasPriceFinal.unsafeGet.toHex.stripLeadingZeros)
|
||||
|
||||
var buildTxResponse: JsonNode
|
||||
let err = status_wallet.buildTransaction(buildTxResponse, chainId, $prepareTxResponse.result)
|
||||
if err.len > 0:
|
||||
error "error occurred", procName="prepareTxForBuyingStickers", msg = err
|
||||
return
|
||||
|
||||
return buildTxResponse
|
||||
except Exception as e:
|
||||
error "error occurred", procName="prepareTxForBuyingStickers", msg = e.msg
|
||||
|
||||
proc signBuyingStickersTxLocally*(self: Service, data, account, hashedPasssword: string): string =
|
||||
try:
|
||||
var response: JsonNode
|
||||
let err = status_wallet.signMessage(response, data, account, hashedPasssword)
|
||||
if err.len > 0 or response.isNil:
|
||||
error "error occurred", procName="signBuyingStickersTxLocally", msg = err
|
||||
return
|
||||
return response.getStr()
|
||||
except Exception as e:
|
||||
error "error occurred", procName="signBuyingStickersTxLocally", msg = e.msg
|
||||
|
||||
proc sendBuyingStickersTxWithSignatureAndWatch*(self: Service, chainId: int, txData: JsonNode, packId: string,
|
||||
signature: string): StickerBuyResultArgs =
|
||||
result = StickerBuyResultArgs(chainId: chainId)
|
||||
try:
|
||||
if txData.isNil:
|
||||
result.error = "txData is nil"
|
||||
error "error occurred", procName="sendBuyingStickersTxWithSignatureAndWatch", msg = result.error
|
||||
return
|
||||
if not txData.hasKey("from") or txData["from"].getStr().len == 0:
|
||||
result.error = "from address is empty"
|
||||
error "error occurred", procName="sendBuyingStickersTxWithSignatureAndWatch", msg = result.error
|
||||
return
|
||||
if not txData.hasKey("to") or txData["to"].getStr().len == 0:
|
||||
result.error = "to address is empty"
|
||||
error "error occurred", procName="sendBuyingStickersTxWithSignatureAndWatch", msg = result.error
|
||||
return
|
||||
|
||||
var finalSignature = signature
|
||||
if finalSignature.startsWith("0x"):
|
||||
finalSignature = finalSignature[2..^1]
|
||||
|
||||
var txResponse: JsonNode
|
||||
let err = status_wallet.sendTransactionWithSignature(txResponse, chainId, $PendingTransactionTypeDto.BuyStickerPack,
|
||||
$txData, finalSignature)
|
||||
if err.len > 0 or txResponse.isNil:
|
||||
result.error = err
|
||||
error "error occurred", procName="sendBuyingStickersTxWithSignatureAndWatch", msg = result.error
|
||||
return
|
||||
|
||||
let
|
||||
transactionHash = txResponse.getStr()
|
||||
fromAddress = txData["from"].getStr()
|
||||
toAddress = txData["to"].getStr()
|
||||
|
||||
let addPendingResponse = status_stickers.addPending(chainId, packId)
|
||||
if not addPendingResponse.error.isNil:
|
||||
result.error = addPendingResponse.error.message
|
||||
error "error occurred", procName="sendBuyingStickersTxWithSignatureAndWatch", msg = result.error
|
||||
return
|
||||
|
||||
let sntContract = self.getStatusToken()
|
||||
self.transactionService.watchTransaction(
|
||||
transactionHash,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
$PendingTransactionTypeDto.BuyStickerPack,
|
||||
packId,
|
||||
chainId,
|
||||
)
|
||||
|
||||
result.txHash = transactionHash
|
||||
except Exception as e:
|
||||
result.error = e.msg
|
||||
error "error occurred", procName="sendBuyingStickersTxWithSignatureAndWatch", msg = result.error
|
@ -7,7 +7,7 @@ export response_type
|
||||
logScope:
|
||||
topics = "rpc"
|
||||
|
||||
## we guard majority db calls which may occure during Profile KeyPair migration
|
||||
## we guard majority db calls which may occure during Profile KeyPair migration
|
||||
## (if there is a need we can guard other non rpc calls as well in the same way)
|
||||
var DB_BLOCKED_DUE_TO_PROFILE_MIGRATION* = false
|
||||
|
||||
@ -61,23 +61,6 @@ proc callPrivateRPC*(
|
||||
}
|
||||
return makePrivateRpcCall(methodName, inputJSON)
|
||||
|
||||
proc signMessage*(rpcParams: string): string =
|
||||
return $status_go.signMessage(rpcParams)
|
||||
|
||||
proc signTypedData*(data: string, address: string, password: string): string =
|
||||
return $status_go.signTypedData(data, address, password)
|
||||
|
||||
proc sendTransaction*(chainId: int, inputJSON: string, password: string): RpcResponse[JsonNode]
|
||||
{.raises: [RpcException, ValueError, Defect, SerializationError].} =
|
||||
try:
|
||||
var hashed_password = "0x" & $keccak_256.digest(password)
|
||||
let rpcResponseRaw = status_go.sendTransactionWithChainId(chainId, inputJSON, hashed_password)
|
||||
result = Json.decode(rpcResponseRaw, RpcResponse[JsonNode])
|
||||
except Exception as e:
|
||||
error "error sending tx", inputJSON, exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
|
||||
proc migrateKeyStoreDir*(account: string, hashedPassword: string, oldKeystoreDir: string, multiaccountKeystoreDir: string)
|
||||
{.raises: [RpcException, ValueError, Defect, SerializationError].} =
|
||||
try:
|
||||
@ -85,5 +68,3 @@ proc migrateKeyStoreDir*(account: string, hashedPassword: string, oldKeystoreDir
|
||||
except Exception as e:
|
||||
error "error migrating keystore dir", account, exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
|
@ -15,9 +15,6 @@ proc getNativeChainBalance*(chainId: int, address: string): RpcResponse[JsonNode
|
||||
let payload = %* [address, "latest"]
|
||||
return core.callPrivateRPCWithChainId("eth_getBalance", chainId, payload)
|
||||
|
||||
proc sendTransaction*(chainId: int, transactionData: string, password: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
core.sendTransaction(chainId, transactionData, password)
|
||||
|
||||
# This is the replacement of the `call` function
|
||||
proc doEthCall*(payload = %* []): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
core.callPrivateRPC("eth_call", payload)
|
||||
|
@ -11,6 +11,10 @@ proc pending*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* []
|
||||
return core.callPrivateRPC("stickers_pending", payload)
|
||||
|
||||
proc addPending*(chainId: int, packId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, packId]
|
||||
result = core.callPrivateRPC("stickers_addPending", payload)
|
||||
|
||||
proc installed*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* []
|
||||
return core.callPrivateRPC("stickers_installed", payload)
|
||||
@ -50,3 +54,7 @@ proc clearRecentStickers*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
proc removePending*(packId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [packId]
|
||||
return core.callPrivateRPC("stickers_removePending", payload)
|
||||
|
||||
proc prepareTxForBuyingStickers*(chainId: int, address: string, packId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, address, packId]
|
||||
result = core.callPrivateRPC("stickers_buyPrepareTx", payload)
|
||||
|
76
src/backend/wallet.nim
Normal file
76
src/backend/wallet.nim
Normal file
@ -0,0 +1,76 @@
|
||||
import json, json_serialization, logging
|
||||
import core, response_type
|
||||
from ./gen import rpc
|
||||
import status_go
|
||||
|
||||
rpc(signMessage, "wallet"):
|
||||
message: string
|
||||
address: string
|
||||
hashedPassword: string
|
||||
|
||||
rpc(buildTransaction, "wallet"):
|
||||
chainId: int
|
||||
sendTxArgsJson: string
|
||||
|
||||
rpc(sendTransactionWithSignature, "wallet"):
|
||||
chainId: int
|
||||
txType: string
|
||||
sendTxArgsJson: string
|
||||
signature: string
|
||||
|
||||
proc isErrorResponse(rpcResponse: RpcResponse[JsonNode]): bool =
|
||||
if not rpcResponse.error.isNil:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc prepareResponse(resultOut: var JsonNode, rpcResponse: RpcResponse[JsonNode]): string =
|
||||
if isErrorResponse(rpcResponse):
|
||||
return rpcResponse.error.message
|
||||
if rpcResponse.result.isNil:
|
||||
return "no result"
|
||||
resultOut = rpcResponse.result
|
||||
|
||||
## Signs the provided message with the provided account using the provided hashed password, performs `crypto.Sign`
|
||||
## `resultOut` represents a json object that contains the signature if the call was successful, or `nil`
|
||||
## `message` is the message to sign
|
||||
## `address` is the address to sign with
|
||||
## `hashedPassword` is the hashed password to sign with
|
||||
## returns the error message if any, or an empty string
|
||||
proc signMessage*(resultOut: var JsonNode, message: string, address: string, hashedPassword: string): string =
|
||||
try:
|
||||
let response = signMessage(message, address, hashedPassword)
|
||||
return prepareResponse(resultOut, response)
|
||||
except Exception as e:
|
||||
warn e.msg
|
||||
return e.msg
|
||||
|
||||
|
||||
## Builds the tx with the provided tx args and chain id
|
||||
## `resultOut` represents a json object that corresponds to the status go `transfer.TxResponse` type, or `nil` if the call was unsuccessful
|
||||
## `chainId` is the chain id of the network
|
||||
## `txArgsJSON` is the json string of the tx, corresponds to the status go `transactions.SendTxArgs` type
|
||||
## returns the error message if any, or an empty string
|
||||
proc buildTransaction*(resultOut: var JsonNode, chainId: int, sendTxArgsJson: string): string =
|
||||
try:
|
||||
let response = buildTransaction(chainId, sendTxArgsJson)
|
||||
return prepareResponse(resultOut, response)
|
||||
except Exception as e:
|
||||
warn e.msg
|
||||
return e.msg
|
||||
|
||||
|
||||
## Sends the tx with signature on provided chain, setting tx type
|
||||
## `resultOut` represents a json object that contains the tx hash if the call was successful, or `nil`
|
||||
## `chainId` is the chain id of the network
|
||||
## `txType` is the type of the tx, corresponds to the status go `transactions.PendingTrxType` type
|
||||
## `txArgsJSON` is the json string of the tx, corresponding to the status go `transactions.SendTxArgs` type
|
||||
## `signature` is the signature of the tx
|
||||
## returns the error message if any, or an empty string
|
||||
proc sendTransactionWithSignature*(resultOut: var JsonNode, chainId: int, txType: string, sendTxArgsJson: string,
|
||||
signature: string): string =
|
||||
try:
|
||||
let response = sendTransactionWithSignature(chainId, txType, sendTxArgsJson, signature)
|
||||
return prepareResponse(resultOut, response)
|
||||
except Exception as e:
|
||||
warn e.msg
|
||||
return e.msg
|
Loading…
x
Reference in New Issue
Block a user