From fa6b1f5ee0b09d946eb835e6e7a9135a72b26e09 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Tue, 24 Dec 2024 10:28:01 +0100 Subject: [PATCH] chore(communities-wallet): various improvements on community related transaction flows These changes should simplify the community related tx handlings on the client side, align it with tx flows that we already have for other sending types and make it maintainable. Related issue: #16810 --- src/app/boot/app_controller.nim | 5 +- .../core/signals/remote_signals/community.nim | 4 +- src/app/global/utils.nim | 3 + src/app/modules/main/communities/module.nim | 5 +- .../main/communities/tokens/controller.nim | 170 ++-- .../main/communities/tokens/io_interface.nim | 68 +- .../main/communities/tokens/module.nim | 343 +++++-- .../modules/main/communities/tokens/view.nim | 57 +- src/app/modules/main/controller.nim | 8 +- src/app/modules/main/io_interface.nim | 4 +- src/app/modules/main/module.nim | 62 +- src/app/modules/main/view.nim | 7 + .../main/wallet_section/send/controller.nim | 10 +- .../main/wallet_section/send/io_interface.nim | 3 - .../modules/main/wallet_section/send/view.nim | 2 +- .../service/community_tokens/async_tasks.nim | 217 +---- .../dto/deployment_parameters.nim | 45 +- .../service/community_tokens/service.nim | 837 ++++++++++-------- src/app_service/service/network/service.nim | 5 + src/app_service/service/transaction/dto.nim | 30 +- src/app_service/service/transaction/dtoV2.nim | 4 + .../transaction/router_transactions_dto.nim | 42 +- .../service/transaction/service.nim | 107 ++- .../service/wallet_account/service.nim | 1 - src/backend/community_tokens.nim | 86 +- src/backend/eth.nim | 66 +- src/backend/wallet.nim | 105 ++- .../src/StatusQ/Core/Utils/Subscription.qml | 1 - .../StatusQ/Core/Utils/SubscriptionBroker.qml | 58 +- .../helpers/AirdropFeesSubscriber.qml | 35 +- .../helpers/DeployFeesSubscriber.qml | 4 + .../helpers/SingleFeeSubscriber.qml | 30 +- .../helpers/TransactionFeesBroker.qml | 143 ++- .../panels/AirdropsSettingsPanel.qml | 10 +- .../panels/MintTokensSettingsPanel.qml | 25 +- .../views/CommunitySettingsView.qml | 201 ++--- .../Communities/views/EditAirdropView.qml | 25 +- .../views/EditCommunityTokenView.qml | 33 +- .../Communities/views/MintedTokensView.qml | 4 +- .../dapps/internal/TransactionFeesBroker.qml | 2 - ui/app/mainui/AppMain.qml | 78 +- ui/app/mainui/Popups.qml | 4 +- .../shared/stores/CommunityTokensStore.qml | 108 +-- ui/imports/shared/stores/CurrenciesStore.qml | 25 + ui/imports/utils/Constants.qml | 7 + vendor/status-go | 2 +- 46 files changed, 1818 insertions(+), 1273 deletions(-) diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index f19d633d4e2..580fc1e3c05 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -205,7 +205,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.communityService = community_service.newService(statusFoundation.events, statusFoundation.threadpool, result.chatService, result.activityCenterService, result.messageService) result.rampService = ramp_service.newService(statusFoundation.events, statusFoundation.threadpool) - result.transactionService = transaction_service.newService(statusFoundation.events, statusFoundation.threadpool, result.networkService, result.settingsService, result.tokenService) + result.transactionService = transaction_service.newService(statusFoundation.events, statusFoundation.threadpool, + result.currencyService, result.networkService, result.settingsService, result.tokenService) result.profileService = profile_service.newService(statusFoundation.events, statusFoundation.threadpool, result.settingsService) result.stickersService = stickers_service.newService( statusFoundation.events, @@ -234,7 +235,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.settingsService, result.walletAccountService, result.transactionService, result.networkService, result.tokenService) result.tokensService = tokens_service.newService(statusFoundation.events, statusFoundation.threadpool, - result.transactionService, result.tokenService, result.settingsService, result.walletAccountService, + result.networkService, result.transactionService, result.tokenService, result.settingsService, result.walletAccountService, result.activityCenterService, result.communityService, result.currencyService) result.providerService = provider_service.newService(statusFoundation.events, statusFoundation.threadpool, result.ensService) result.networkConnectionService = network_connection_service.newService(statusFoundation.events, diff --git a/src/app/core/signals/remote_signals/community.nim b/src/app/core/signals/remote_signals/community.nim index 043756ac38c..233c283c69c 100644 --- a/src/app/core/signals/remote_signals/community.nim +++ b/src/app/core/signals/remote_signals/community.nim @@ -79,7 +79,7 @@ type DiscordChannelImportFinishedSignal* = ref object of Signal channelId*: string type CommunityTokenTransactionStatusChangedSignal* = ref object of Signal - transactionType*: string + sendType*: int success*: bool hash*: string communityToken*: CommunityTokenDto @@ -276,7 +276,7 @@ proc downloadingHistoryArchivesFinishedFromEvent*(T: type HistoryArchivesSignal, proc fromEvent*(T: type CommunityTokenTransactionStatusChangedSignal, event: JsonNode): CommunityTokenTransactionStatusChangedSignal = result = CommunityTokenTransactionStatusChangedSignal() result.signalType = SignalType.CommunityTokenTransactionStatusChanged - result.transactionType = event["event"]{"transactionType"}.getStr() + result.sendType = event["event"]{"sendType"}.getInt() result.success = event["event"]{"success"}.getBool() result.hash = event["event"]{"hash"}.getStr() if event["event"].hasKey("communityToken"): diff --git a/src/app/global/utils.nim b/src/app/global/utils.nim index f52c24fe0a3..19ac6a6f992 100644 --- a/src/app/global/utils.nim +++ b/src/app/global/utils.nim @@ -48,6 +48,9 @@ QtObject: weiValue = fromHex(Stuint[256], weiValue).toString() return conversion.wei2Eth(weiValue, decimals) + proc hexToDec*(self: Utils, hexValue: string): string {.slot.} = + return fromHex(Stuint[256], hexValue).toString() + proc hex2Ascii*(self: Utils, value: string): string {.slot.} = result = string.fromBytes(hexToSeqByte(value)) diff --git a/src/app/modules/main/communities/module.nim b/src/app/modules/main/communities/module.nim index 8dbe101446d..7bee6fcff85 100644 --- a/src/app/modules/main/communities/module.nim +++ b/src/app/modules/main/communities/module.nim @@ -128,7 +128,8 @@ proc newModule*( walletAccountService, keycardService, ) - result.communityTokensModule = community_tokens_module.newCommunityTokensModule(result, events, communityTokensService, transactionService, networksService, communityService) + result.communityTokensModule = community_tokens_module.newCommunityTokensModule(result, events, walletAccountService, + communityTokensService, transactionService, networksService, communityService, keycardService) result.moduleLoaded = false result.events = events result.curatedCommunitiesLoaded = false @@ -955,7 +956,7 @@ proc applyPermissionResponse*(self: Module, communityId: string, permissions: Ta if not aCriteriaChanged: continue - + let updatedTokenPermissionItem = initTokenPermissionItem( tokenPermissionItem.id, tokenPermissionItem.`type`, diff --git a/src/app/modules/main/communities/tokens/controller.nim b/src/app/modules/main/communities/tokens/controller.nim index 1d66af99e14..d78695d3ecd 100644 --- a/src/app/modules/main/communities/tokens/controller.nim +++ b/src/app/modules/main/communities/tokens/controller.nim @@ -1,43 +1,50 @@ -import stint +import stint, uuids, chronicles import ./io_interface as community_tokens_module_interface import app_service/service/community_tokens/service as community_tokens_service +import app_service/service/wallet_account/service as wallet_account_service import app_service/service/transaction/service as transaction_service import app_service/service/network/service as networks_service import app_service/service/community/service as community_service +import app_service/service/keycard/service as keycard_service import app_service/service/network/network_item -import app_service/common/types import app/core/signals/types import app/core/eventemitter -import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module +import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module - -const UNIQUE_DEPLOY_COLLECTIBLES_COMMUNITY_TOKENS_MODULE_IDENTIFIER* = "communityTokensModule-DeployCollectibles" +const UNIQUE_COMMUNITY_TOKENS_MODULE_IDENTIFIER* = "communityTokensModuleIdentifier" type Controller* = ref object of RootObj - communityTokensModule: community_tokens_module_interface.AccessInterface + delegate: community_tokens_module_interface.AccessInterface events: EventEmitter + walletAccountService: wallet_account_service.Service communityTokensService: community_tokens_service.Service transactionService: transaction_service.Service networksService: networks_service.Service communityService: community_service.Service + keycardService: keycard_service.Service + connectionKeycardResponse: UUID proc newCommunityTokensController*( - communityTokensModule: community_tokens_module_interface.AccessInterface, + delegate: community_tokens_module_interface.AccessInterface, events: EventEmitter, + walletAccountService: wallet_account_service.Service, communityTokensService: community_tokens_service.Service, transactionService: transaction_service.Service, networksService: networks_service.Service, - communityService: community_service.Service + communityService: community_service.Service, + keycardService: keycard_service.Service ): Controller = result = Controller() - result.communityTokensModule = communityTokensModule + result.delegate = delegate result.events = events + result.walletAccountService = walletAccountService result.communityTokensService = communityTokensService result.transactionService = transactionService result.networksService = networksService result.communityService = communityService + result.keycardService = keycardService proc delete*(self: Controller) = discard @@ -45,75 +52,86 @@ proc delete*(self: Controller) = proc init*(self: Controller) = self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args): let args = SharedKeycarModuleArgs(e) - if args.uniqueIdentifier != UNIQUE_DEPLOY_COLLECTIBLES_COMMUNITY_TOKENS_MODULE_IDENTIFIER: + if args.uniqueIdentifier != UNIQUE_COMMUNITY_TOKENS_MODULE_IDENTIFIER: return - self.communityTokensModule.onUserAuthenticated(args.password) - self.events.on(SIGNAL_COMPUTE_DEPLOY_FEE) do(e:Args): - let args = ComputeFeeArgs(e) - self.communityTokensModule.onDeployFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode, args.requestId) + self.delegate.onUserAuthenticated(args.password, args.pin) self.events.on(SIGNAL_COMPUTE_SELF_DESTRUCT_FEE) do(e:Args): let args = ComputeFeeArgs(e) - self.communityTokensModule.onSelfDestructFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode, args.requestId) + self.delegate.onSelfDestructFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode, args.requestId) self.events.on(SIGNAL_COMPUTE_BURN_FEE) do(e:Args): let args = ComputeFeeArgs(e) - self.communityTokensModule.onBurnFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode, args.requestId) + self.delegate.onBurnFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode, args.requestId) self.events.on(SIGNAL_COMPUTE_SET_SIGNER_FEE) do(e:Args): let args = ComputeFeeArgs(e) - self.communityTokensModule.onSetSignerFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode, args.requestId) + self.delegate.onSetSignerFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode, args.requestId) self.events.on(SIGNAL_COMPUTE_AIRDROP_FEE) do(e:Args): let args = AirdropFeesArgs(e) - self.communityTokensModule.onAirdropFeesComputed(args) - self.events.on(SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STARTED) do(e: Args): - let args = CommunityTokenDeploymentArgs(e) - self.communityTokensModule.onCommunityTokenDeployStateChanged(args.communityToken.communityId, args.communityToken.chainId, args.transactionHash, args.communityToken.deployState) - self.events.on(SIGNAL_COMMUNITY_TOKEN_DEPLOY_STATUS) do(e: Args): - let args = CommunityTokenDeployedStatusArgs(e) - self.communityTokensModule.onCommunityTokenDeployStateChanged(args.communityId, args.chainId, args.transactionHash, args.deployState) - self.events.on(SIGNAL_OWNER_TOKEN_DEPLOYMENT_STARTED) do(e: Args): - let args = OwnerTokenDeploymentArgs(e) - self.communityTokensModule.onOwnerTokenDeployStarted(args.ownerToken.communityId, args.ownerToken.chainId, args.transactionHash) - self.events.on(SIGNAL_OWNER_TOKEN_DEPLOY_STATUS) do(e: Args): - let args = OwnerTokenDeployedStatusArgs(e) - self.communityTokensModule.onOwnerTokenDeployStateChanged(args.communityId, args.chainId, args.transactionHash, args.deployState) + self.delegate.onAirdropFeesComputed(args) self.events.on(SIGNAL_REMOTE_DESTRUCT_STATUS) do(e: Args): let args = RemoteDestructArgs(e) - self.communityTokensModule.onRemoteDestructStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status) + self.delegate.onRemoteDestructStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status) self.events.on(SIGNAL_BURN_STATUS) do(e: Args): let args = RemoteDestructArgs(e) - self.communityTokensModule.onBurnStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status) + self.delegate.onBurnStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status) self.events.on(SIGNAL_AIRDROP_STATUS) do(e: Args): let args = AirdropArgs(e) - self.communityTokensModule.onAirdropStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status) + self.delegate.onAirdropStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status) self.events.on(SIGNAL_OWNER_TOKEN_RECEIVED) do(e: Args): let args = OwnerTokenReceivedArgs(e) - self.communityTokensModule.onOwnerTokenReceived(args.communityId, args.communityName, args.chainId, args.contractAddress) + self.delegate.onOwnerTokenReceived(args.communityId, args.communityName, args.chainId, args.contractAddress) self.events.on(SIGNAL_COMMUNITY_TOKEN_RECEIVED) do(e: Args): let args = CommunityTokenReceivedArgs(e) if args.isWatchOnlyAccount: return - self.communityTokensModule.onCommunityTokenReceived(args.name, args.symbol, args.image, args.communityId, args.communityName, $args.amount, args.chainId, args.txHash, args.isFirst, args.tokenType, args.accountName, args.accountAddress) + self.delegate.onCommunityTokenReceived(args.name, args.symbol, args.image, args.communityId, args.communityName, $args.amount, args.chainId, args.txHash, args.isFirst, args.tokenType, args.accountName, args.accountAddress) self.events.on(SIGNAL_SET_SIGNER_STATUS) do(e: Args): let args = SetSignerArgs(e) - self.communityTokensModule.onSetSignerStateChanged(args.communityId, args.chainId, args.transactionHash, args.status) + self.delegate.onSetSignerStateChanged(args.communityId, args.chainId, args.transactionHash, args.status) self.events.on(SIGNAL_COMMUNITY_LOST_OWNERSHIP) do(e: Args): let args = CommunityIdArgs(e) - self.communityTokensModule.onLostOwnership(args.communityId) + self.delegate.onLostOwnership(args.communityId) self.events.on(SIGNAL_OWNER_TOKEN_OWNER_ADDRESS) do(e: Args): let args = OwnerTokenOwnerAddressArgs(e) - self.communityTokensModule.onOwnerTokenOwnerAddress(args.chainId, args.contractAddress, args.address, args.addressName) + self.delegate.onOwnerTokenOwnerAddress(args.chainId, args.contractAddress, args.address, args.addressName) self.events.on(SIGNAL_OWNER_TOKEN_SENT) do(e: Args): let args = OwnerTokenSentArgs(e) - self.communityTokensModule.onSendOwnerTokenStateChanged(args.chainId, args.txHash, args.tokenName, args.status) + self.delegate.onSendOwnerTokenStateChanged(args.chainId, args.txHash, args.tokenName, args.status) self.events.on(SIGNAL_COMMUNITY_TOKENS_CHANGED) do(e:Args): self.communityTokensService.getAllCommunityTokensAsync() - -proc deployContract*(self: Controller, communityId: string, addressFrom: string, password: string, deploymentParams: DeploymentParameters, chainId: int) = - self.communityTokensService.deployContract(communityId, addressFrom, password, deploymentParams, chainId) - -proc deployOwnerContracts*(self: Controller, communityId: string, addressFrom: string, password: string, - ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters, chainId: int) = - self.communityTokensService.deployOwnerContracts(communityId, addressFrom, password, ownerDeploymentParams, - masterDeploymentParams, chainId) + self.events.on(SIGNAL_SUGGESTED_ROUTES_READY) do(e:Args): + let args = SuggestedRoutesArgs(e) + self.delegate.suggestedRoutesReady(args.uuid, args.sendType, args.totalCostEthCurrency, args.totalCostFiatCurrency, + args.costPerPath, args.errCode, args.errDescription) + self.events.on(SIGNAL_SIGN_ROUTER_TRANSACTIONS) do(e:Args): + let args = RouterTransactionsForSigningArgs(e) + self.delegate.prepareSignaturesForTransactions(args.data) + self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args): + let args = TransactionArgs(e) + var + txHash = "" + isApprovalTx = false + toAddress = "" + if not args.sentTransaction.isNil: + txHash = args.sentTransaction.hash + isApprovalTx = args.sentTransaction.approvalTx + toAddress = args.sentTransaction.toAddress + self.delegate.onTransactionSent( + args.sendDetails.uuid, + SendType(args.sendDetails.sendType), + args.sendDetails.fromChain, + isApprovalTx, + txHash, + toAddress, + if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: "" + ) + +proc storeDeployedContract*(self: Controller, sendType: SendType, addressFrom: string, addressTo: string, chainId: int, + txHash: string, deploymentParams: DeploymentParameters) = + self.communityTokensService.storeDeployedContract(sendType, addressFrom, addressTo, chainId, txHash, deploymentParams) + +proc storeDeployedOwnerContract*(self: Controller, addressFrom: string, chainId: int, txHash: string, + ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters) = + self.communityTokensService.storeDeployedOwnerContract(addressFrom, chainId, txHash, ownerDeploymentParams, masterDeploymentParams) proc removeCommunityToken*(self: Controller, communityId: string, chainId: int, address: string) = self.communityTokensService.removeCommunityToken(communityId, chainId, address) @@ -121,11 +139,9 @@ proc removeCommunityToken*(self: Controller, communityId: string, chainId: int, proc refreshCommunityToken*(self: Controller, chainId: int, address: string) = self.communityTokensService.refreshCommunityToken(chainId, address) -proc airdropTokens*(self: Controller, communityId: string, password: string, tokensAndAmounts: seq[CommunityTokenAndAmount], walletAddresses: seq[string], addressFrom: string) = - self.communityTokensService.airdropTokens(communityId, password, tokensAndAmounts, walletAddresses, addressFrom) -proc computeAirdropFee*(self: Controller, tokensAndAmounts: seq[CommunityTokenAndAmount], walletAddresses: seq[string], addressFrom: string, requestId: string) = - self.communityTokensService.computeAirdropFee(tokensAndAmounts, walletAddresses, addressFrom, requestId) +proc computeAirdropFee*(self: Controller, uuid: string, tokensAndAmounts: seq[CommunityTokenAndAmount], walletAddresses: seq[string], addressFrom: string) = + self.communityTokensService.computeAirdropFee(uuid, tokensAndAmounts, walletAddresses, addressFrom) proc selfDestructCollectibles*(self: Controller, communityId: string, password: string, walletAndAmounts: seq[WalletAndAmount], contractUniqueKey: string, addressFrom: string) = self.communityTokensService.selfDestructCollectibles(communityId, password, walletAndAmounts, contractUniqueKey, addressFrom) @@ -136,21 +152,23 @@ proc burnTokens*(self: Controller, communityId: string, password: string, contra proc setSigner*(self: Controller, password: string, communityId: string, chainId: int, contractAddress: string, addressFrom: string) = self.communityTokensService.setSigner(password, communityId, chainId, contractAddress, addressFrom) -proc authenticateUser*(self: Controller, keyUid = "") = - let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_DEPLOY_COLLECTIBLES_COMMUNITY_TOKENS_MODULE_IDENTIFIER, keyUid: keyUid) +proc authenticate*(self: Controller, keyUid = "") = + let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_COMMUNITY_TOKENS_MODULE_IDENTIFIER, + keyUid: keyUid) self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data) proc getCommunityTokens*(self: Controller, communityId: string): seq[CommunityTokenDto] = return self.communityTokensService.getCommunityTokens(communityId) -proc computeDeployFee*(self: Controller, chainId: int, accountAddress: string, tokenType: TokenType, requestId: string) = - self.communityTokensService.computeDeployFee(chainId, accountAddress, tokenType, requestId) +proc computeDeployTokenFee*(self: Controller, uuid: string, chainId: int, accountAddress: string, communityId: string, deploymentParams: DeploymentParameters) = + self.communityTokensService.computeDeployTokenFee(uuid, chainId, accountAddress, communityId, deploymentParams) proc computeSetSignerFee*(self: Controller, chainId: int, contractAddress: string, addressFrom: string, requestId: string) = self.communityTokensService.computeSetSignerFee(chainId, contractAddress, addressFrom, requestId) -proc computeDeployOwnerContractsFee*(self: Controller, chainId: int, accountAddress: string, communityId: string, ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters, requestId: string) = - self.communityTokensService.computeDeployOwnerContractsFee(chainId, accountAddress, communityId, ownerDeploymentParams, masterDeploymentParams, requestId) +proc computeDeployOwnerContractsFee*(self: Controller, uuid: string, chainId: int, accountAddress: string, communityId: string, +ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters) = + self.communityTokensService.computeDeployOwnerContractsFee(uuid, chainId, accountAddress, communityId, ownerDeploymentParams, masterDeploymentParams) proc computeSelfDestructFee*(self: Controller, walletAndAmountList: seq[WalletAndAmount], contractUniqueKey: string, addressFrom: string, requestId: string) = self.communityTokensService.computeSelfDestructFee(walletAndAmountList, contractUniqueKey, addressFrom, requestId) @@ -178,3 +196,41 @@ proc declineOwnership*(self: Controller, communityId: string) = proc asyncGetOwnerTokenOwnerAddress*(self: Controller, chainId: int, contractAddress: string) = self.communityTokensService.asyncGetOwnerTokenOwnerAddress(chainId, contractAddress) + +proc stopSuggestedRoutesAsyncCalculation*(self: Controller) = + self.communityTokensService.stopSuggestedRoutesAsyncCalculation() + +proc getKeypairByAccountAddress*(self: Controller, address: string): KeypairDto = + return self.walletAccountService.getKeypairByAccountAddress(address) + +proc buildTransactionsFromRoute*(self: Controller, uuid: string): string = + return self.communityTokensService.buildTransactionsFromRoute(uuid) + +proc sendRouterTransactionsWithSignatures*(self: Controller, uuid: string, signatures: TransactionsSignatures): string = + return self.communityTokensService.sendRouterTransactionsWithSignatures(uuid, signatures) + +proc signMessage*(self: Controller, address: string, hashedPassword: string, hashedMessage: string): tuple[res: string, err: string] = + return self.communityTokensService.signMessage(address, hashedPassword, hashedMessage) + +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: + error "trying to use keycard in other than the signing a community related transaction flow" + #TODO: notifify about error + # self.delegate.transactionWasSent(uuid = "", chainId = 0, approvalTx = false, txHash = "", error = "trying to use keycard in the other than the signing a transaction flow") + return + self.delegate.onTransactionSigned(args.flowType, args.flowEvent) + +proc cancelCurrentFlow*(self: Controller) = + self.keycardService.cancelCurrentFlow() + +proc runSignFlow*(self: Controller, pin, bip44Path, txHash: string) = + self.cancelCurrentFlow() + self.connectKeycardReponseSignal() + self.keycardService.startSignFlow(bip44Path, txHash, pin) \ No newline at end of file diff --git a/src/app/modules/main/communities/tokens/io_interface.nim b/src/app/modules/main/communities/tokens/io_interface.nim index 47926ca61a1..c429c665cb2 100644 --- a/src/app/modules/main/communities/tokens/io_interface.nim +++ b/src/app/modules/main/communities/tokens/io_interface.nim @@ -1,7 +1,10 @@ -import ../../../../../app_service/service/community_tokens/service -import ../../../../../app_service/service/community/dto/community -import ../../../../../app_service/common/types -import ../../../shared_models/currency_amount +import app_service/service/transaction/dto +import app_service/service/transaction/router_transactions_dto +import app_service/service/community_tokens/service +import app_service/service/community/dto/community +import app_service/common/types +import app/modules/shared_models/currency_amount +from app_service/service/keycard/service import KeycardEvent type AccessInterface* {.pure inheritable.} = ref object of RootObj @@ -12,10 +15,9 @@ method delete*(self: AccessInterface) {.base.} = method load*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method airdropTokens*(self: AccessInterface, communityId: string, tokensJsonString: string, walletsJsonString: string, addressFrom: string) {.base.} = - raise newException(ValueError, "No implementation available") -method computeAirdropFee*(self: AccessInterface, communityId: string, tokensJsonString: string, walletsJsonString: string, addressFrom: string, requestId: string) {.base.} = +method computeAirdropFee*(self: AccessInterface, uuid: string, communityId: string, tokensJsonString: string, + walletsJsonString: string, addressFrom: string) {.base.} = raise newException(ValueError, "No implementation available") method selfDestructCollectibles*(self: AccessInterface, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string) {.base.} = @@ -27,25 +29,32 @@ method burnTokens*(self: AccessInterface, communityId: string, contractUniqueKey method setSigner*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, addressFrom: string) {.base.} = raise newException(ValueError, "No implementation available") -method deployCollectibles*(self: AccessInterface, communityId: string, address: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, transferable: bool, - selfDestruct: bool, chainId: int, imageCropInfoJson: string) {.base.} = - raise newException(ValueError, "No implementation available") +method computeDeployCollectiblesFee*(self: AccessInterface, uuid: string, communityId: string, fromAddress: string, + name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, transferable: bool, + selfDestruct: bool, chainId: int, imageCropInfoJson: string) {.base.} = + raise newException(ValueError, "No implementation available") -method deployAssets*(self: AccessInterface, communityId: string, address: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, decimals: int, - chainId: int, imageCropInfoJson: string) {.base.} = - raise newException(ValueError, "No implementation available") +method computeDeployAssetsFee*(self: AccessInterface, uuid: string, communityId: string, address: string, name: string, + symbol: string, description: string, supply: string, infiniteSupply: bool, decimals: int, chainId: int, + imageCropInfoJson: string) {.base.} = + raise newException(ValueError, "No implementation available") -method deployOwnerToken*(self: AccessInterface, communityId: string, fromAddress: string, ownerName: string, ownerSymbol: string, ownerDescription: string, - masterName: string, masterSymbol: string, masterDescription: string, chainId: int, imageCropInfoJson: string) {.base.} = +method computeDeployTokenOwnerFee*(self: AccessInterface, uuid: string, communityId: string, fromAddress: string, + ownerName: string, ownerSymbol: string, ownerDescription: string, masterName: string, masterSymbol: string, + masterDescription: string, chainId: int, imageCropInfoJson: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method authenticateAndTransfer*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} = +method onUserAuthenticated*(self: AccessInterface, password: string, pin: string) {.base.} = raise newException(ValueError, "No implementation available") method resetTempValues*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method computeDeployFee*(self: AccessInterface, communityId: string, chainId: int, accountAddress: string, tokenType: TokenType, isOwnerDeployment: bool, requestId: string) {.base.} = +method computeDeployFee*(self: AccessInterface, uuid: string, communityId: string, chainId: int, accountAddress: string, + tokenType: TokenType, isOwnerDeployment: bool) {.base.} = raise newException(ValueError, "No implementation available") method computeSetSignerFee*(self: AccessInterface, chainId: int, contractAddress: string, addressFrom: string, requestId: string) {.base.} = @@ -57,8 +66,6 @@ method computeSelfDestructFee*(self: AccessInterface, collectiblesToBurnJsonStri method computeBurnFee*(self: AccessInterface, contractUniqueKey: string, amount: string, addressFrom: string, requestId: string) {.base.} = raise newException(ValueError, "No implementation available") -method onDeployFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) {.base.} = - raise newException(ValueError, "No implementation available") method onSelfDestructFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) {.base.} = raise newException(ValueError, "No implementation available") @@ -72,14 +79,8 @@ method onBurnFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fi method onSetSignerFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) {.base.} = raise newException(ValueError, "No implementation available") -method onCommunityTokenDeployStateChanged*(self: AccessInterface, communityId: string, chainId: int, transactionHash: string, deployState: DeployState) {.base.} = - raise newException(ValueError, "No implementation available") -method onOwnerTokenDeployStateChanged*(self: AccessInterface, communityId: string, chainId: int, transactionHash: string, deployState: DeployState) {.base.} = - raise newException(ValueError, "No implementation available") -method onOwnerTokenDeployStarted*(self: AccessInterface, communityId: string, chainId: int, transactionHash: string) {.base.} = - raise newException(ValueError, "No implementation available") method onRemoteDestructStateChanged*(self: AccessInterface, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) {.base.} = raise newException(ValueError, "No implementation available") @@ -119,3 +120,20 @@ method onOwnerTokenOwnerAddress*(self: AccessInterface, chainId: int, contractAd method asyncGetOwnerTokenDetails*(self: AccessInterface, communityId: string) {.base.} = raise newException(ValueError, "No implementation available") + +method suggestedRoutesReady*(self: AccessInterface, uuid: string, sendType: SendType, ethCurrency: CurrencyAmount, + fiatCurrency: CurrencyAmount, costPerPath: seq[CostPerPath], errCode: string, errDescription: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method stopUpdatesForSuggestedRoute*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method prepareSignaturesForTransactions*(self:AccessInterface, txForSigning: RouterTransactionsForSigningDto) {.base.} = + raise newException(ValueError, "No implementation available") + +method onTransactionSigned*(self: AccessInterface, keycardFlowType: string, keycardEvent: KeycardEvent) {.base.} = + raise newException(ValueError, "No implementation available") + +method onTransactionSent*(self: AccessInterface, uuid: string, sendType: SendType, chainId: int, approvalTx: bool, + txHash: string, toAddress: string, error: string) {.base.} = + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/communities/tokens/module.nim b/src/app/modules/main/communities/tokens/module.nim index aa23dc1cdf1..3305788774e 100644 --- a/src/app/modules/main/communities/tokens/module.nim +++ b/src/app/modules/main/communities/tokens/module.nim @@ -1,14 +1,20 @@ -import NimQml, json, stint, strutils, chronicles, tables - -import ../../../../../app_service/service/community_tokens/service as community_tokens_service -import ../../../../../app_service/service/transaction/service as transaction_service -import ../../../../../app_service/service/network/service as networks_service -import ../../../../../app_service/service/community/service as community_service -import ../../../../../app_service/service/accounts/utils as utl -import ../../../../../app_service/common/types -import ../../../../core/eventemitter -import ../../../../global/global_singleton -import ../../../shared_models/currency_amount +import NimQml +import tables, json, sequtils, sugar, stint, strutils, chronicles + +import app_service/service/wallet_account/service as wallet_account_service +import app_service/service/community_tokens/service as community_tokens_service +import app_service/service/transaction/service as transaction_service +import app_service/service/network/service as networks_service +import app_service/service/community/service as community_service +import app_service/service/accounts/utils as utl +import app_service/service/keycard/service as keycard_service +import app_service/service/keycard/constants as keycard_constants +import app_service/common/types +import app_service/common/utils +import app_service/common/wallet_constants +import app/core/eventemitter +import app/global/global_singleton +import app/modules/shared_models/currency_amount import ../io_interface as parent_interface import ./io_interface, ./view , ./controller @@ -30,6 +36,9 @@ type controller: Controller view: View viewVariant: QVariant + tempUuid: string + tempPin: string + tempPassword: string tempTokenAndAmountList: seq[CommunityTokenAndAmount] tempWalletAndAmountList: seq[WalletAndAmount] tempAddressFrom: string @@ -44,19 +53,29 @@ type tempOwnerDeploymentParams: DeploymentParameters tempMasterDeploymentParams: DeploymentParameters tempOwnerTokenCommunity: CommunityDto + tempResolvedSignatures: TransactionsSignatures + tempTxHashBeingProcessed: string proc newCommunityTokensModule*( parent: parent_interface.AccessInterface, events: EventEmitter, + walletAccountService: wallet_account_service.Service, communityTokensService: community_tokens_service.Service, transactionService: transaction_service.Service, networksService: networks_service.Service, - communityService: community_service.Service): Module = + communityService: community_service.Service, + keycardService: keycard_service.Service + ): Module = result = Module() result.parent = parent result.view = newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newCommunityTokensController(result, events, communityTokensService, transactionService, networksService, communityService) + result.controller = controller.newCommunityTokensController(result, events, walletAccountService, communityTokensService, + transactionService, networksService, communityService, keycardService) + +## Forward declarations +proc buildTransactionsFromRoute(self: Module) + method delete*(self: Module) = self.view.delete @@ -64,6 +83,9 @@ method delete*(self: Module) = self.controller.delete method resetTempValues(self:Module) = + self.tempUuid = "" + self.tempPin = "" + self.tempPassword = "" self.tempAddressFrom = "" self.tempCommunityId = "" self.tempDeploymentParams = DeploymentParameters() @@ -76,18 +98,192 @@ method resetTempValues(self:Module) = self.tempContractUniqueKey = "" self.tempOwnerDeploymentParams = DeploymentParameters() self.tempMasterDeploymentParams = DeploymentParameters() + self.tempOwnerTokenCommunity = CommunityDto() + self.tempResolvedSignatures.clear() + self.tempTxHashBeingProcessed = "" method load*(self: Module) = singletonInstance.engine.setRootContextProperty("communityTokensModule", self.viewVariant) self.controller.init() self.view.load() -proc authenticate(self: Module) = - if singletonInstance.userProfile.getIsKeycardUser(): - let keyUid = singletonInstance.userProfile.getKeyUid() - self.controller.authenticateUser(keyUid) +proc createOwnerAndMasterDeploymentParams(self: Module, communityId: string): (DeploymentParameters, DeploymentParameters) = + let communityDto = self.controller.getCommunityById(communityId) + let commName = communityDto.name + let commNameShort = try: commName[0 .. 2].toUpper except: commName.toUpper + return ( + DeploymentParameters( + communityId: communityId, + name: "Owner-" & commName, + symbol: "OWN" & commNameShort, + supply: stint.u256("1"), + infiniteSupply: false, + transferable: true, + remoteSelfDestruct: false, + tokenUri: utl.changeCommunityKeyCompression(communityId) & "/" + ), + DeploymentParameters( + communityId: communityId, + name: "TMaster-" & commName, + symbol: "TM" & commNameShort, + infiniteSupply: true, + transferable: false, + remoteSelfDestruct: true, + tokenUri: utl.changeCommunityKeyCompression(communityId) & "/" + ) + ) + +method authenticateAndTransfer*(self: Module) = + self.tempResolvedSignatures.clear() + + if self.tempUuid.len == 0: + error "No uuid to authenticate and transfer" + #TODO: notify about error + return + + if self.tempAddressFrom.len == 0: + error "No address to send from" + #TODO: notify about error + return + + let kp = self.controller.getKeypairByAccountAddress(self.tempAddressFrom) + if kp.migratedToKeycard(): + let accounts = kp.accounts.filter(acc => cmpIgnoreCase(acc.address, self.tempAddressFrom) == 0) + if accounts.len != 1: + error "cannot resolve selected account to send from among known keypair accounts" + #TODO: notify about error + return + self.controller.authenticate(kp.keyUid) else: - self.controller.authenticateUser() + self.controller.authenticate() + +method onUserAuthenticated*(self: Module, password: string, pin: string) = + if password.len == 0: + error "No password provided from authentication" + #TODO: notify about error + self.resetTempValues() + else: + self.tempPin = pin + self.tempPassword = password + self.buildTransactionsFromRoute() + +proc buildTransactionsFromRoute(self: Module) = + let err = self.controller.buildTransactionsFromRoute(self.tempUuid) + if err.len > 0: + error "Error building transactions from route", err = err + #TODO: notify about error + self.resetTempValues() + +proc sendSignedTransactions*(self: Module) = + try: + # check if all transactions are signed + for _, (r, s, v) in self.tempResolvedSignatures.pairs: + if r.len == 0 or s.len == 0 or v.len == 0: + raise newException(CatchableError, "not all transactions are signed") + + let err = self.controller.sendRouterTransactionsWithSignatures(self.tempUuid, self.tempResolvedSignatures) + if err.len > 0: + raise newException(CatchableError, "sending transaction failed: " & err) + except Exception as e: + error "sendSignedTransactions failed: ", msg=e.msg + #TODO: notify about error + self.resetTempValues() + +proc signOnKeycard(self: Module) = + self.tempTxHashBeingProcessed = "" + for h, (r, s, v) in self.tempResolvedSignatures.pairs: + if r.len != 0 and s.len != 0 and v.len != 0: + continue + self.tempTxHashBeingProcessed = h + var txForKcFlow = self.tempTxHashBeingProcessed + if txForKcFlow.startsWith("0x"): + txForKcFlow = txForKcFlow[2..^1] + self.controller.runSignFlow(self.tempPin, self.tempAddressFrom, txForKcFlow) + break + if self.tempTxHashBeingProcessed.len == 0: + self.sendSignedTransactions() + +proc getRSVFromSignature(self: Module, signature: string): (string, string, string) = + let finalSignature = singletonInstance.utils.removeHexPrefix(signature) + if finalSignature.len != SIGNATURE_LEN: + return ("", "", "") + let r = finalSignature[0..63] + let s = finalSignature[64..127] + let v = finalSignature[128..129] + return (r, s, v) + +method prepareSignaturesForTransactions*(self:Module, txForSigning: RouterTransactionsForSigningDto) = + var res = "" + try: + if txForSigning.sendDetails.uuid != self.tempUuid: + raise newException(CatchableError, "preparing signatures for transactions are not matching the initial request") + if txForSigning.signingDetails.hashes.len == 0: + raise newException(CatchableError, "no transaction hashes to be signed") + if txForSigning.signingDetails.keyUid == "" or txForSigning.signingDetails.address == "" or txForSigning.signingDetails.addressPath == "": + raise newException(CatchableError, "preparing signatures for transactions failed") + + if txForSigning.signingDetails.signOnKeycard: + self.tempAddressFrom = txForSigning.signingDetails.addressPath + for h in txForSigning.signingDetails.hashes: + self.tempResolvedSignatures[h] = ("", "", "") + self.signOnKeycard() + else: + var finalPassword = self.tempPassword + if not singletonInstance.userProfile.getIsKeycardUser(): + finalPassword = hashPassword(self.tempPassword) + for h in txForSigning.signingDetails.hashes: + self.tempResolvedSignatures[h] = ("", "", "") + var + signature = "" + err: string + (signature, err) = self.controller.signMessage(txForSigning.signingDetails.address, finalPassword, h) + if err.len > 0: + raise newException(CatchableError, "signing transaction failed: " & err) + self.tempResolvedSignatures[h] = self.getRSVFromSignature(signature) + self.sendSignedTransactions() + except Exception as e: + error "signMessageWithCallback failed: ", msg=e.msg + #TODO: notify about error + self.resetTempValues() + +method onTransactionSigned*(self: Module, keycardFlowType: string, keycardEvent: KeycardEvent) = + if keycardFlowType != keycard_constants.ResponseTypeValueKeycardFlowResult: + let err = "unexpected error while keycard signing transaction" + error "error", err=err + # TODO: notify about error + self.resetTempValues() + return + self.tempResolvedSignatures[self.tempTxHashBeingProcessed] = (keycardEvent.txSignature.r, keycardEvent.txSignature.s, keycardEvent.txSignature.v) + self.signOnKeycard() + +method onTransactionSent*(self: Module, uuid: string, sendType: SendType, chainId: int, approvalTx: bool, txHash: string, + toAddress: string, error: string) = + if error.len > 0: + error "Error sending transaction", error = error + #TODO: notify about error + self.resetTempValues() + return + if self.tempContractAction == ContractAction.Deploy: + self.controller.storeDeployedContract(sendType, self.tempAddressFrom, toAddress, chainId, txHash, self.tempDeploymentParams) + return + if self.tempContractAction == ContractAction.Airdrop: + # self.controller.airdropTokens(self.tempCommunityId, password, self.tempTokenAndAmountList, self.tempWalletAddresses, self.tempAddressFrom) + return + if self.tempContractAction == ContractAction.SelfDestruct: + # self.controller.selfDestructCollectibles(self.tempCommunityId, password, self.tempWalletAndAmountList, self.tempContractUniqueKey, self.tempAddressFrom) + return + if self.tempContractAction == ContractAction.Burn: + # self.controller.burnTokens(self.tempCommunityId, password, self.tempContractUniqueKey, self.tempAmount, self.tempAddressFrom) + return + if self.tempContractAction == ContractAction.DeployOwnerToken: + # self.controller.deployOwnerContracts(self.tempCommunityId, self.tempAddressFrom, password, + # self.tempOwnerDeploymentParams, self.tempMasterDeploymentParams, self.tempChainId) + self.controller.storeDeployedOwnerContract(self.tempAddressFrom, chainId, txHash, self.tempOwnerDeploymentParams, self.tempMasterDeploymentParams) + return + if self.tempContractAction == ContractAction.SetSigner: + # self.controller.setSigner(password, self.tempCommunityId, self.tempChainId, self.tempContractAddress, self.tempAddressFrom) + return + error "Unknown contract action" proc getTokenAndAmountList(self: Module, communityId: string, tokensJsonString: string): seq[CommunityTokenAndAmount] = try: @@ -117,21 +313,14 @@ proc getOwnerAndMasterTokensAddresses(self: Module, communityId: string, chainId let masterTokenAddress = self.getTokenAddressFromPermissions(communityDto, chainId, TokenPermissionType.BecomeTokenMaster) return (ownerTokenAddress, masterTokenAddress, ownerTokenAddress != "" and masterTokenAddress != "") -method airdropTokens*(self: Module, communityId: string, tokensJsonString: string, walletsJsonString: string, addressFrom: string) = - self.tempTokenAndAmountList = self.getTokenAndAmountList(communityId, tokensJsonString) - if len(self.tempTokenAndAmountList) == 0: - return - self.tempWalletAddresses = walletsJsonString.parseJson.to(seq[string]) - self.tempCommunityId = communityId - self.tempAddressFrom = addressFrom - self.tempContractAction = ContractAction.Airdrop - self.authenticate() - -method computeAirdropFee*(self: Module, communityId: string, tokensJsonString: string, walletsJsonString: string, addressFrom: string, requestId: string) = +method computeAirdropFee*(self: Module, uuid: string, communityId: string, tokensJsonString: string, walletsJsonString: string, + addressFrom: string) = let tokenAndAmountList = self.getTokenAndAmountList(communityId, tokensJsonString) if len(tokenAndAmountList) == 0: return - self.controller.computeAirdropFee(tokenAndAmountList, walletsJsonString.parseJson.to(seq[string]), addressFrom, requestId) + self.tempUuid = uuid + self.tempAddressFrom = addressFrom + self.controller.computeAirdropFee(uuid, tokenAndAmountList, walletsJsonString.parseJson.to(seq[string]), addressFrom) proc getWalletAndAmountListFromJson(self: Module, collectiblesToBurnJsonString: string): seq[WalletAndAmount] = let collectiblesToBurnJson = collectiblesToBurnJsonString.parseJson @@ -146,7 +335,6 @@ method selfDestructCollectibles*(self: Module, communityId: string, collectibles self.tempContractUniqueKey = contractUniqueKey self.tempAddressFrom = addressFrom self.tempContractAction = ContractAction.SelfDestruct - self.authenticate() method burnTokens*(self: Module, communityId: string, contractUniqueKey: string, amount: string, addressFrom: string) = self.tempCommunityId = communityId @@ -154,7 +342,6 @@ method burnTokens*(self: Module, communityId: string, contractUniqueKey: string, self.tempAmount = amount.parse(Uint256) self.tempAddressFrom = addressFrom self.tempContractAction = ContractAction.Burn - self.authenticate() method setSigner*(self: Module, communityId: string, chainId: int, contractAddress: string, addressFrom: string) = self.tempCommunityId = communityId @@ -162,14 +349,16 @@ method setSigner*(self: Module, communityId: string, chainId: int, contractAddre self.tempContractAddress = contractAddress self.tempAddressFrom = addressFrom self.tempContractAction = ContractAction.SetSigner - self.authenticate() -method deployCollectibles*(self: Module, communityId: string, fromAddress: string, name: string, symbol: string, description: string, - supply: string, infiniteSupply: bool, transferable: bool, selfDestruct: bool, chainId: int, imageCropInfoJson: string) = +method computeDeployCollectiblesFee*(self: Module, uuid: string, communityId: string, fromAddress: string, name: string, + symbol: string, description: string, supply: string, infiniteSupply: bool, transferable: bool, selfDestruct: bool, + chainId: int, imageCropInfoJson: string) = + # TODO: move this check to service and send route ready signal to update the UI and notifiy the user let (ownerTokenAddress, masterTokenAddress, isDeployed) = self.getOwnerAndMasterTokensAddresses(communityId, chainId) if not isDeployed: error "Owner token and master token not deployed" return + self.tempUuid = uuid self.tempAddressFrom = fromAddress self.tempCommunityId = communityId self.tempChainId = chainId @@ -191,26 +380,18 @@ method deployCollectibles*(self: Module, communityId: string, fromAddress: strin self.tempDeploymentParams.communityId = communityId self.tempContractAction = ContractAction.Deploy - self.authenticate() + self.controller.computeDeployTokenFee(uuid, chainId, fromAddress, communityId, self.tempDeploymentParams) -proc createOwnerAndMasterDeploymentParams(self: Module, communityId: string): (DeploymentParameters, DeploymentParameters) = - let communityDto = self.controller.getCommunityById(communityId) - let commName = communityDto.name - let commNameShort = try: commName[0 .. 2].toUpper except: commName.toUpper - return (DeploymentParameters(name: "Owner-" & commName, symbol: "OWN" & commNameShort, supply: stint.u256("1"), - infiniteSupply: false, transferable: true, remoteSelfDestruct: false, - tokenUri: utl.changeCommunityKeyCompression(communityId) & "/", communityId: communityId), - DeploymentParameters(name: "TMaster-" & commName, symbol: "TM" & commNameShort, infiniteSupply: true, - transferable: false, remoteSelfDestruct: true, - tokenUri: utl.changeCommunityKeyCompression(communityId) & "/", communityId: communityId)) - -method deployOwnerToken*(self: Module, communityId: string, fromAddress: string, ownerName: string, ownerSymbol: string, ownerDescription: string, - masterName: string, masterSymbol: string, masterDescription: string, chainId: int, imageCropInfoJson: string) = +method computeDeployTokenOwnerFee*(self: Module, uuid: string, communityId: string, fromAddress: string, ownerName: string, + ownerSymbol: string, ownerDescription: string, masterName: string, masterSymbol: string, masterDescription: string, + chainId: int, imageCropInfoJson: string) = + # TODO: move this check to service and send route ready signal to update the UI and notifiy the user let (_, _, isDeployed) = self.getOwnerAndMasterTokensAddresses(communityId, chainId) if isDeployed: error "Owner token and master token are deployed or pending" return + self.tempUuid = uuid self.tempAddressFrom = fromAddress self.tempCommunityId = communityId self.tempChainId = chainId @@ -224,14 +405,17 @@ method deployOwnerToken*(self: Module, communityId: string, fromAddress: string, self.tempMasterDeploymentParams.tokenType = TokenType.ERC721 self.tempMasterDeploymentParams.base64image = base65Image self.tempContractAction = ContractAction.DeployOwnerToken - self.authenticate() -method deployAssets*(self: Module, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, decimals: int, - chainId: int, imageCropInfoJson: string) = + self.controller.computeDeployOwnerContractsFee(uuid, chainId, fromAddress, communityId, self.tempOwnerDeploymentParams, self.tempMasterDeploymentParams) + +method computeDeployAssetsFee*(self: Module, uuid: string, communityId: string, fromAddress: string, name: string, symbol: string, description: string, + supply: string, infiniteSupply: bool, decimals: int, chainId: int, imageCropInfoJson: string) = + # TODO: move this check to service and send route ready signal to update the UI and notifiy the user let (ownerTokenAddress, masterTokenAddress, isDeployed) = self.getOwnerAndMasterTokensAddresses(communityId, chainId) if not isDeployed: error "Owner token and master token not deployed" return + self.tempUuid = uuid self.tempAddressFrom = fromAddress self.tempCommunityId = communityId self.tempChainId = chainId @@ -252,7 +436,7 @@ method deployAssets*(self: Module, communityId: string, fromAddress: string, nam self.tempDeploymentParams.communityId = communityId self.tempContractAction = ContractAction.Deploy - self.authenticate() + self.controller.computeDeployTokenFee(uuid, chainId, fromAddress, communityId, self.tempDeploymentParams) method removeCommunityToken*(self: Module, communityId: string, chainId: int, address: string) = self.controller.removeCommunityToken(communityId, chainId, address) @@ -260,28 +444,6 @@ method removeCommunityToken*(self: Module, communityId: string, chainId: int, ad method refreshCommunityToken*(self: Module, chainId: int, address: string) = self.controller.refreshCommunityToken(chainId, address) -method onUserAuthenticated*(self: Module, password: string) = - defer: self.resetTempValues() - if password.len == 0: - discard - #TODO signalize somehow - else: - if self.tempContractAction == ContractAction.Deploy: - self.controller.deployContract(self.tempCommunityId, self.tempAddressFrom, password, self.tempDeploymentParams, self.tempChainId) - elif self.tempContractAction == ContractAction.Airdrop: - self.controller.airdropTokens(self.tempCommunityId, password, self.tempTokenAndAmountList, self.tempWalletAddresses, self.tempAddressFrom) - elif self.tempContractAction == ContractAction.SelfDestruct: - self.controller.selfDestructCollectibles(self.tempCommunityId, password, self.tempWalletAndAmountList, self.tempContractUniqueKey, self.tempAddressFrom) - elif self.tempContractAction == ContractAction.Burn: - self.controller.burnTokens(self.tempCommunityId, password, self.tempContractUniqueKey, self.tempAmount, self.tempAddressFrom) - elif self.tempContractAction == ContractAction.DeployOwnerToken: - self.controller.deployOwnerContracts(self.tempCommunityId, self.tempAddressFrom, password, - self.tempOwnerDeploymentParams, self.tempMasterDeploymentParams, self.tempChainId) - elif self.tempContractAction == ContractAction.SetSigner: - self.controller.setSigner(password, self.tempCommunityId, self.tempChainId, self.tempContractAddress, self.tempAddressFrom) - -method onDeployFeeComputed*(self: Module, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) = - self.view.updateDeployFee(ethCurrency, fiatCurrency, errorCode.int, responseId) method onSelfDestructFeeComputed*(self: Module, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) = self.view.updateSelfDestructFee(ethCurrency, fiatCurrency, errorCode.int, responseId) @@ -295,12 +457,6 @@ method onBurnFeeComputed*(self: Module, ethCurrency: CurrencyAmount, fiatCurrenc method onSetSignerFeeComputed*(self: Module, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) = self.view.updateSetSignerFee(ethCurrency, fiatCurrency, errorCode.int, responseId) -method computeDeployFee*(self: Module, communityId: string, chainId: int, accountAddress: string, tokenType: TokenType, isOwnerDeployment: bool, requestId: string) = - if isOwnerDeployment: - let (ownerDeploymentParams, masterDeploymentParams) = self.createOwnerAndMasterDeploymentParams(communityId) - self.controller.computeDeployOwnerContractsFee(chainId, accountAddress, communityId, ownerDeploymentParams, masterDeploymentParams, requestId) - else: - self.controller.computeDeployFee(chainId, accountAddress, tokenType, requestId) method computeSetSignerFee*(self: Module, chainId: int, contractAddress: string, addressFrom: string, requestId: string) = self.controller.computeSetSignerFee(chainId, contractAddress, addressFrom, requestId) @@ -320,17 +476,8 @@ proc getChainName(self: Module, chainId: int): string = let network = self.controller.getNetworkByChainId(chainId) result = if network != nil: network.chainName else: "" -method onCommunityTokenDeployStateChanged*(self: Module, communityId: string, chainId: int, transactionHash: string, deployState: DeployState) = - let url = self.createUrl(chainId, transactionHash) - self.view.emitDeploymentStateChanged(communityId, deployState.int, url) -method onOwnerTokenDeployStateChanged*(self: Module, communityId: string, chainId: int, transactionHash: string, deployState: DeployState) = - let url = self.createUrl(chainId, transactionHash) - self.view.emitOwnerTokenDeploymentStateChanged(communityId, deployState.int, url) -method onOwnerTokenDeployStarted*(self: Module, communityId: string, chainId: int, transactionHash: string) = - let url = self.createUrl(chainId, transactionHash) - self.view.emitOwnerTokenDeploymentStarted(communityId, url) method onRemoteDestructStateChanged*(self: Module, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) = let url = self.createUrl(chainId, transactionHash) @@ -393,3 +540,19 @@ method onOwnerTokenOwnerAddress*(self: Module, chainId: int, contractAddress: st "contractAddress": contractAddress } self.view.setOwnerTokenDetails($jsonObj) + + +method suggestedRoutesReady*(self: Module, uuid: string, sendType: SendType, ethCurrency: CurrencyAmount, + fiatCurrency: CurrencyAmount, costPerPath: seq[CostPerPath], errCode: string, errDescription: string) = + if sendType != SendType.CommunityBurn and + sendType != SendType.CommunityDeployAssets and + sendType != SendType.CommunityDeployCollectibles and + sendType != SendType.CommunityDeployOwnerToken and + sendType != SendType.CommunityMintTokens and + sendType != SendType.CommunityRemoteBurn and + sendType != SendType.CommunitySetSignerPubKey: + return + self.view.emitSuggestedRoutesReadySignal(uuid, ethCurrency, fiatCurrency, %costPerPath, errCode, errDescription) + +method stopUpdatesForSuggestedRoute*(self: Module) = + self.controller.stopSuggestedRoutesAsyncCalculation() \ No newline at end of file diff --git a/src/app/modules/main/communities/tokens/view.nim b/src/app/modules/main/communities/tokens/view.nim index 9ffb55402eb..de3b4b86489 100644 --- a/src/app/modules/main/communities/tokens/view.nim +++ b/src/app/modules/main/communities/tokens/view.nim @@ -2,8 +2,6 @@ import NimQml, json, strutils, sequtils import ./io_interface as community_tokens_module_interface import app/modules/shared_models/currency_amount -import app_service/common/conversion -import app_service/common/types QtObject: type @@ -22,14 +20,26 @@ QtObject: result.QObject.setup result.communityTokensModule = communityTokensModule - proc deployCollectible*(self: View, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, transferable: bool, selfDestruct: bool, chainId: int, imageCropInfoJson: string) {.slot.} = - self.communityTokensModule.deployCollectibles(communityId, fromAddress, name, symbol, description, supply, infiniteSupply, transferable, selfDestruct, chainId, imageCropInfoJson) + proc authenticateAndTransfer*(self: View) {.slot.} = + self.communityTokensModule.authenticateAndTransfer() - proc deployAssets*(self: View, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, decimals: int, chainId: int, imageCropInfoJson: string) {.slot.} = - self.communityTokensModule.deployAssets(communityId, fromAddress, name, symbol, description, supply, infiniteSupply, decimals, chainId, imageCropInfoJson) + proc computeDeployCollectiblesFee*(self: View, uuid: string, communityId: string, fromAddress: string, name: string, + symbol: string, description: string, supply: string, infiniteSupply: bool, transferable: bool, selfDestruct: bool, + chainId: int, imageCropInfoJson: string) {.slot.} = + self.communityTokensModule.computeDeployCollectiblesFee(uuid, communityId, fromAddress, name, symbol, description, + supply, infiniteSupply, transferable, selfDestruct, chainId, imageCropInfoJson) - proc deployOwnerToken*(self:View, communityId: string, fromAddress: string, ownerName: string, ownerSymbol: string, ownerDescription: string, masterName: string, masterSymbol: string, masterDescription: string, chainId: int, imageCropInfoJson: string) {.slot.} = - self.communityTokensModule.deployOwnerToken(communityId, fromAddress, ownerName, ownerSymbol, ownerDescription, masterName, masterSymbol, masterDescription, chainId, imageCropInfoJson) + proc computeDeployAssetsFee*(self: View, uuid: string, communityId: string, fromAddress: string, name: string, + symbol: string, description: string, supply: string, infiniteSupply: bool, decimals: int, chainId: int, + imageCropInfoJson: string) {.slot.} = + self.communityTokensModule.computeDeployAssetsFee(uuid, communityId, fromAddress, name, symbol, description, supply, + infiniteSupply, decimals, chainId, imageCropInfoJson) + + proc computeDeployTokenOwnerFee*(self:View, uuid: string, communityId: string, fromAddress: string, ownerName: string, + ownerSymbol: string, ownerDescription: string, masterName: string, masterSymbol: string, masterDescription: string, + chainId: int, imageCropInfoJson: string) {.slot.} = + self.communityTokensModule.computeDeployTokenOwnerFee(uuid, communityId, fromAddress, ownerName, ownerSymbol, + ownerDescription, masterName, masterSymbol, masterDescription, chainId, imageCropInfoJson) proc removeCommunityToken*(self: View, communityId: string, chainId: int, address: string) {.slot.} = self.communityTokensModule.removeCommunityToken(communityId, chainId, address) @@ -37,11 +47,10 @@ QtObject: proc refreshCommunityToken*(self: View, chainId: int, address: string) {.slot.} = self.communityTokensModule.refreshCommunityToken(chainId, address) - proc airdropTokens*(self: View, communityId: string, tokensJsonString: string, walletsJsonString: string, addressFrom: string) {.slot.} = - self.communityTokensModule.airdropTokens(communityId, tokensJsonString, walletsJsonString, addressFrom) - proc computeAirdropFee*(self: View, communityId: string, tokensJsonString: string, walletsJsonString: string, addressFrom: string, requestId: string) {.slot.} = - self.communityTokensModule.computeAirdropFee(communityId, tokensJsonString, walletsJsonString, addressFrom, requestId) + proc computeAirdropFee*(self: View, uuid: string, communityId: string, tokensJsonString: string, + walletsJsonString: string, addressFrom: string) {.slot.} = + self.communityTokensModule.computeAirdropFee(uuid, communityId, tokensJsonString, walletsJsonString, addressFrom) proc selfDestructCollectibles*(self: View, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string) {.slot.} = self.communityTokensModule.selfDestructCollectibles(communityId, collectiblesToBurnJsonString, contractUniqueKey, addressFrom) @@ -52,7 +61,6 @@ QtObject: proc setSigner*(self: View, communityId: string, chainId: int, contractAddress: string, addressFrom: string) {.slot.} = self.communityTokensModule.setSigner(communityId, chainId, contractAddress, addressFrom) - proc deployFeeUpdated*(self: View, ethCurrency: QVariant, fiatCurrency: QVariant, errorCode: int, responseId: string) {.signal.} proc selfDestructFeeUpdated*(self: View, ethCurrency: QVariant, fiatCurrency: QVariant, errorCode: int, responseId: string) {.signal.} proc airdropFeesUpdated*(self: View, json: string) {.signal.} proc burnFeeUpdated*(self: View, ethCurrency: QVariant, fiatCurrency: QVariant, errorCode: int, responseId: string) {.signal.} @@ -63,8 +71,6 @@ QtObject: proc ownershipNodeLost*(self: View, communityId: string, communityName: string) {.signal.} proc sendOwnerTokenStateChanged*(self: View, tokenName: string, status: int, url: string) {.signal.} - proc computeDeployFee*(self: View, communityId: string, chainId: int, accountAddress: string, tokenType: int, isOwnerDeployment: bool, requestId: string) {.slot.} = - self.communityTokensModule.computeDeployFee(communityId, chainId, accountAddress, intToEnum(tokenType, TokenType.Unknown), isOwnerDeployment, requestId) proc computeSetSignerFee*(self: View, chainId: int, contractAddress: string, addressFrom: string, requestId: string) {.slot.} = self.communityTokensModule.computeSetSignerFee(chainId, contractAddress, addressFrom, requestId) @@ -78,8 +84,6 @@ QtObject: proc declineOwnership*(self: View, communityId: string) {.slot.} = self.communityTokensModule.declineOwnership(communityId) - proc updateDeployFee*(self: View, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: int, responseId: string) = - self.deployFeeUpdated(newQVariant(ethCurrency), newQVariant(fiatCurrency), errorCode, responseId) proc updateBurnFee*(self: View, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: int, responseId: string) = self.burnFeeUpdated(newQVariant(ethCurrency), newQVariant(fiatCurrency), errorCode, responseId) @@ -93,9 +97,6 @@ QtObject: proc updateAirdropFees*(self: View, args: JsonNode) = self.airdropFeesUpdated($args) - proc deploymentStateChanged*(self: View, communityId: string, status: int, url: string) {.signal.} - proc emitDeploymentStateChanged*(self: View, communityId: string, status: int, url: string) = - self.deploymentStateChanged(communityId, status, url) proc remoteDestructStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) {.signal.} proc emitRemoteDestructStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) = @@ -109,13 +110,7 @@ QtObject: proc emitAirdropStateChanged*(self: View, communityId: string, tokenName: string, chainName: string, status: int, url: string) = self.airdropStateChanged(communityId, tokenName, chainName, status, url) - proc ownerTokenDeploymentStarted*(self: View, communityId: string, url: string) {.signal.} - proc emitOwnerTokenDeploymentStarted*(self: View, communityId: string, url: string) = - self.ownerTokenDeploymentStarted(communityId, url) - proc ownerTokenDeploymentStateChanged*(self: View, communityId: string, status: int, url: string) {.signal.} - proc emitOwnerTokenDeploymentStateChanged*(self: View, communityId: string, status: int, url: string) = - self.ownerTokenDeploymentStateChanged(communityId, status, url) proc emitOwnerTokenReceived*(self: View, communityId: string, communityName: string, chainId: int, contractAddress: string) = self.ownerTokenReceived(communityId, communityName, chainId, contractAddress) @@ -145,3 +140,13 @@ QtObject: QtProperty[string] ownerTokenDetails: read = getOwnerTokenDetails notify = ownerTokenDetailsChanged + + proc suggestedRoutesReady(self: View, uuid: string, ethCurrency: QVariant, fiatCurrency: QVariant, + costPerPath: string, errCode: string, errDescription: string) {.signal.} + proc emitSuggestedRoutesReadySignal*(self: View, uuid: string, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, + costPerPath: JsonNode, errCode: string, errDescription: string) = + self.suggestedRoutesReady(uuid, newQVariant(ethCurrency), newQVariant(fiatCurrency), + $costPerPath, errCode, errDescription) + + proc stopUpdatesForSuggestedRoute*(self: View) {.slot.} = + self.communityTokensModule.stopUpdatesForSuggestedRoute() \ No newline at end of file diff --git a/src/app/modules/main/controller.nim b/src/app/modules/main/controller.nim index adf9261888a..b5f1defd98a 100644 --- a/src/app/modules/main/controller.nim +++ b/src/app/modules/main/controller.nim @@ -351,13 +351,13 @@ proc init*(self: Controller) = self.events.on(SIGNAL_COMMUNITY_MY_REQUEST_ADDED) do(e: Args): self.delegate.onMyRequestAdded(); - self.events.on(SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STARTED) do(e: Args): + self.events.on(SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STORED) do(e: Args): let args = CommunityTokenDeploymentArgs(e) - self.delegate.onCommunityTokenDeploymentStarted(args.communityToken) + self.delegate.onCommunityTokenDeploymentStored(args.communityToken, args.error) - self.events.on(SIGNAL_OWNER_TOKEN_DEPLOYMENT_STARTED) do(e: Args): + self.events.on(SIGNAL_OWNER_TOKEN_DEPLOYMENT_STORED) do(e: Args): let args = OwnerTokenDeploymentArgs(e) - self.delegate.onOwnerTokensDeploymentStarted(args.ownerToken, args.masterToken) + self.delegate.onOwnerTokensDeploymentStored(args.ownerToken, args.masterToken, args.error) self.events.on(SIGNAL_COMMUNITY_TOKEN_DEPLOY_STATUS) do(e: Args): let args = CommunityTokenDeployedStatusArgs(e) diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index f8808ac8f9a..768308d6af6 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -346,10 +346,10 @@ method tryKeycardSync*(self: AccessInterface, keyUid: string, pin: string) {.bas method onSharedKeycarModuleKeycardSyncPurposeTerminated*(self: AccessInterface, lastStepInTheCurrentFlow: bool) {.base.} = raise newException(ValueError, "No implementation available") -method onCommunityTokenDeploymentStarted*(self: AccessInterface, communityToken: CommunityTokenDto) {.base.} = +method onCommunityTokenDeploymentStored*(self: AccessInterface, communityToken: CommunityTokenDto, error: string) {.base.} = raise newException(ValueError, "No implementation available") -method onOwnerTokensDeploymentStarted*(self: AccessInterface, ownerToken: CommunityTokenDto, masterToken: CommunityTokenDto) {.base.} = +method onOwnerTokensDeploymentStored*(self: AccessInterface, ownerToken: CommunityTokenDto, masterToken: CommunityTokenDto, error: string) {.base.} = raise newException(ValueError, "No implementation available") method onCommunityTokenOwnersFetched*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) {.base.} = diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 78682322d70..8071b72ad1c 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -103,6 +103,7 @@ type keychainService: keychain_service.Service networkConnectionService: network_connection_service.Service stickersService: stickers_service.Service + communityTokensService: community_tokens_service.Service walletSectionModule: wallet_section_module.AccessInterface profileSectionModule: profile_section_module.AccessInterface stickersModule: stickers_module.AccessInterface @@ -211,6 +212,7 @@ proc newModule*[T]( result.savedAddressService = savedAddressService result.keychainService = keychainService result.stickersService = stickersService + result.communityTokensService = communityTokensService # Submodules result.chatSectionModules = initOrderedTable[string, chat_section_module.AccessInterface]() @@ -454,6 +456,14 @@ proc sendNotification[T](self: Module[T], status: string, sendDetails: SendDetai error = "" txHash = "" isApprovalTx = false + #community specific details + communityId = "" + communityName = "" + communityAmount = "" + communityAssetName = "" + communityAssetDecimals = 0 + communityOwnerTokenName = "" + communityMasterTokenName = "" if not sendDetails.errorResponse.isNil: error = sendDetails.errorResponse.details @@ -503,6 +513,27 @@ proc sendNotification[T](self: Module[T], status: string, sendDetails: SendDetai if not accDto.isNil: txToName = accDto.name + # check for community details + if not sendDetails.communityParams.isNil: + communityId = sendDetails.communityParams.communityId + communityAmount = sendDetails.communityParams.amount.toString(10) + let item = self.view.model().getItemById(communityId) + if item.id == "": + error = "cannot_resolve_community" + else: + communityName = item.name + + let communityToken = self.communityTokensService.getCommunityTokenFromCache(fromChain, sendDetails.communityParams.tokenContractAddress) + if not communityToken.name.len == 0: + error = "cannot_resolve_community_token" + else: + communityAssetName = communityToken.name + communityAssetDecimals = communityToken.decimals + + if txType == SendType.CommunityDeployOwnerToken: + communityOwnerTokenName = sendDetails.communityParams.ownerTokenParameters.name + communityMasterTokenName = sendDetails.communityParams.masterTokenParameters.name + self.view.showTransactionToast( sendDetails.uuid, sendDetails.sendType, @@ -523,6 +554,13 @@ proc sendNotification[T](self: Module[T], status: string, sendDetails: SendDetai sendDetails.username, sendDetails.publicKey, sendDetails.packId, + communityId, + communityName, + communityAmount, + communityAssetName, + communityAssetDecimals, + communityOwnerTokenName, + communityMasterTokenName, status, error, ) @@ -1348,16 +1386,26 @@ method resolvedENS*[T](self: Module[T], publicKey: string, address: string, uuid else: self.view.emitResolvedENSSignal(publicKey, address, uuid) -method onCommunityTokenDeploymentStarted*[T](self: Module[T], communityToken: CommunityTokenDto) = +method onCommunityTokenDeploymentStored*[T](self: Module[T], communityToken: CommunityTokenDto, error: string) = + if error != "": + error "Cannot update section model, due to error storing community token: ", error + return let item = self.view.model().getItemById(communityToken.communityId) - if item.id != "": - item.appendCommunityToken(self.createTokenItem(communityToken)) + if item.id == "": + error "Cannot update section model, due to missing community with id: ", communityId=communityToken.communityId + return + item.appendCommunityToken(self.createTokenItem(communityToken)) -method onOwnerTokensDeploymentStarted*[T](self: Module[T], ownerToken: CommunityTokenDto, masterToken: CommunityTokenDto) = +method onOwnerTokensDeploymentStored*[T](self: Module[T], ownerToken: CommunityTokenDto, masterToken: CommunityTokenDto, error: string) = + if error != "": + error "Cannot update section model, due to error storing owner tokens: ", error + return let item = self.view.model().getItemById(ownerToken.communityId) - if item.id != "": - item.appendCommunityToken(self.createTokenItem(ownerToken)) - item.appendCommunityToken(self.createTokenItem(masterToken)) + if item.id == "": + error "Cannot update section model, due to missing community with id: ", communityId=ownerToken.communityId + return + item.appendCommunityToken(self.createTokenItem(ownerToken)) + item.appendCommunityToken(self.createTokenItem(masterToken)) method onCommunityTokenRemoved*[T](self: Module[T], communityId: string, chainId: int, address: string) = let item = self.view.model().getItemById(communityId) diff --git a/src/app/modules/main/view.nim b/src/app/modules/main/view.nim index f90bf1d1037..aa66416a534 100644 --- a/src/app/modules/main/view.nim +++ b/src/app/modules/main/view.nim @@ -361,6 +361,13 @@ QtObject: username: string, publicKey: string, packId: string, + communityId: string, + communityName: string, + communityAmount: string, + communityAssetName: string, + communityAssetDecimals: int, + communityOwnerTokenName: string, + communityMasterTokenName: string, status: string, error: string) {.signal.} diff --git a/src/app/modules/main/wallet_section/send/controller.nim b/src/app/modules/main/wallet_section/send/controller.nim index 365e8fdb138..a4e5c1bedfa 100644 --- a/src/app/modules/main/wallet_section/send/controller.nim +++ b/src/app/modules/main/wallet_section/send/controller.nim @@ -55,7 +55,7 @@ proc delete*(self: Controller) = proc init*(self: Controller) = self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args): let args = TransactionArgs(e) - var + var txHash = "" isApprovalTx = false if not args.sentTransaction.isNil: @@ -81,6 +81,14 @@ proc init*(self: Controller) = self.events.on(SIGNAL_SUGGESTED_ROUTES_READY) do(e:Args): let args = SuggestedRoutesArgs(e) + if args.sendType == SendType.CommunityBurn or + args.sendType == SendType.CommunityDeployAssets or + args.sendType == SendType.CommunityDeployCollectibles or + args.sendType == SendType.CommunityDeployOwnerToken or + args.sendType == SendType.CommunityMintTokens or + args.sendType == SendType.CommunityRemoteBurn or + args.sendType == SendType.CommunitySetSignerPubKey: + return self.delegate.suggestedRoutesReady(args.uuid, args.suggestedRoutes, args.errCode, args.errDescription) self.events.on(SIGNAL_SIGN_ROUTER_TRANSACTIONS) do(e:Args): diff --git a/src/app/modules/main/wallet_section/send/io_interface.nim b/src/app/modules/main/wallet_section/send/io_interface.nim index 4df1f267dd3..cf8b37f69a8 100644 --- a/src/app/modules/main/wallet_section/send/io_interface.nim +++ b/src/app/modules/main/wallet_section/send/io_interface.nim @@ -59,9 +59,6 @@ method viewDidLoad*(self: AccessInterface) {.base.} = method authenticateUser*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method onUserAuthenticated*(self: AccessInterface, pin: string, password: string, keyUid: string) {.base.} = - raise newException(ValueError, "No implementation available") - method setSelectedReceiveAccountIndex*(self: AccessInterface, index: int) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/wallet_section/send/view.nim b/src/app/modules/main/wallet_section/send/view.nim index 36fab7fe294..c27741abc8a 100644 --- a/src/app/modules/main/wallet_section/send/view.nim +++ b/src/app/modules/main/wallet_section/send/view.nim @@ -6,7 +6,7 @@ import app_service/service/transaction/dto as transaction_dto import app_service/common/utils as common_utils import app_service/service/eth/utils as eth_utils -from backend/eth import ExtraKeyPackId +from backend/wallet import ExtraKeyPackId QtObject: type diff --git a/src/app_service/service/community_tokens/async_tasks.nim b/src/app_service/service/community_tokens/async_tasks.nim index b86cb2c61b2..7658ad8cd9f 100644 --- a/src/app_service/service/community_tokens/async_tasks.nim +++ b/src/app_service/service/community_tokens/async_tasks.nim @@ -1,21 +1,11 @@ import stint, Tables -include ../../common/json_utils -import ../../../backend/eth -import ../../../backend/community_tokens -import ../../../backend/collectibles -include ../../../app/core/tasks/common -import ../../../app/core/tasks/qt -import ../transaction/dto + +import backend/collectibles +import app/core/tasks/qt import ../community/dto/community -proc tableToJsonArray[A, B](t: var Table[A, B]): JsonNode = - let data = newJArray() - for k,v in t: - data.elems.add(%*{ - "key": k, - "value": v - }) - return data +include app/core/tasks/common +include app_service/common/json_utils proc balanceInfoToTable(jsonNode: JsonNode): Table[string, UInt256] = for chainBalancesPair in jsonNode.pairs(): @@ -26,203 +16,6 @@ proc balanceInfoToTable(jsonNode: JsonNode): Table[string, UInt256] = result[addressTokenBalancesPair.key.toUpper] = amount break -type - AsyncDeployOwnerContractsFeesArg = ref object of QObjectTaskArg - chainId: int - addressFrom: string - requestId: string - ownerParams: JsonNode - masterParams: JsonNode - communityId: string - signerPubKey: string - -proc asyncGetDeployOwnerContractsFeesTask(argEncoded: string) {.gcsafe, nimcall.} = - let arg = decode[AsyncDeployOwnerContractsFeesArg](argEncoded) - try: - var gasTable: Table[ContractTuple, int] # gas per contract - var feeTable: Table[int, SuggestedFeesDto] # fees for chain - - let estimations = community_tokens.deployOwnerTokenEstimate(arg.chainId, arg.addressFrom, arg.ownerParams, arg.masterParams, arg.communityId, arg.signerPubKey).result - gasTable[(arg.chainId, "")] = estimations{"gasUnits"}.getInt - feeTable[arg.chainId] = estimations{"suggestedFees"}.toSuggestedFeesDto() - - arg.finish(%* { - "feeTable": tableToJsonArray(feeTable), - "gasTable": tableToJsonArray(gasTable), - "chainId": arg.chainId, - "addressFrom": arg.addressFrom, - "error": "", - "requestId": arg.requestId, - }) - except Exception as e: - arg.finish(%* { - "error": e.msg, - "requestId": arg.requestId, - }) - -type - AsyncGetDeployFeesArg = ref object of QObjectTaskArg - chainId: int - addressFrom: string - tokenType: TokenType - requestId: string - -proc asyncGetDeployFeesTask(argEncoded: string) {.gcsafe, nimcall.} = - let arg = decode[AsyncGetDeployFeesArg](argEncoded) - try: - var gasTable: Table[ContractTuple, int] # gas per contract - var feeTable: Table[int, SuggestedFeesDto] # fees for chain - - let estimations = if arg.tokenType == TokenType.ERC721: community_tokens.deployCollectiblesEstimate(arg.chainId, arg.addressFrom).result - else: community_tokens.deployAssetsEstimate(arg.chainId, arg.addressFrom).result - gasTable[(arg.chainId, "")] = estimations{"gasUnits"}.getInt - feeTable[arg.chainId] = estimations{"suggestedFees"}.toSuggestedFeesDto() - - arg.finish(%* { - "feeTable": tableToJsonArray(feeTable), - "gasTable": tableToJsonArray(gasTable), - "chainId": arg.chainId, - "addressFrom": arg.addressFrom, - "error": "", - "requestId": arg.requestId, - }) - except Exception as e: - arg.finish(%* { - "error": e.msg, - "requestId": arg.requestId, - }) - -type - AsyncSetSignerFeesArg = ref object of QObjectTaskArg - chainId: int - contractAddress: string - addressFrom: string - newSignerPubKey: string - requestId: string - -proc asyncSetSignerFeesTask(argEncoded: string) {.gcsafe, nimcall.} = - let arg = decode[AsyncSetSignerFeesArg](argEncoded) - try: - var gasTable: Table[ContractTuple, int] # gas per contract - var feeTable: Table[int, SuggestedFeesDto] # fees for chain - - let estimations = community_tokens.estimateSetSignerPubKey(arg.chainId, arg.contractAddress, arg.addressFrom, arg.newSignerPubKey).result - gasTable[(arg.chainId, arg.contractAddress)] = estimations{"gasUnits"}.getInt - feeTable[arg.chainId] = estimations{"suggestedFees"}.toSuggestedFeesDto() - - arg.finish(%* { - "feeTable": tableToJsonArray(feeTable), - "gasTable": tableToJsonArray(gasTable), - "chainId": arg.chainId, - "addressFrom": arg.addressFrom, - "error": "", - "requestId": arg.requestId, - }) - except Exception as e: - arg.finish(%* { - "error": e.msg, - "requestId": arg.requestId, - }) - -type - AsyncGetRemoteBurnFees = ref object of QObjectTaskArg - chainId: int - contractAddress: string - tokenIds: seq[UInt256] - addressFrom: string - requestId: string - -proc asyncGetRemoteBurnFeesTask(argEncoded: string) {.gcsafe, nimcall.} = - let arg = decode[AsyncGetRemoteBurnFees](argEncoded) - try: - var gasTable: Table[ContractTuple, int] # gas per contract - var feeTable: Table[int, SuggestedFeesDto] # fees for chain - - let estimations = community_tokens.estimateRemoteBurn(arg.chainId, arg.contractAddress, arg.addressFrom, arg.tokenIds).result - gasTable[(arg.chainId, arg.contractAddress)] = estimations{"gasUnits"}.getInt - feeTable[arg.chainId] = estimations{"suggestedFees"}.toSuggestedFeesDto() - - arg.finish(%* { - "feeTable": tableToJsonArray(feeTable), - "gasTable": tableToJsonArray(gasTable), - "chainId": arg.chainId, - "addressFrom": arg.addressFrom, - "error": "", - "requestId": arg.requestId, - }) - except Exception as e: - arg.finish(%* { - "error": e.msg, - "requestId": arg.requestId, - }) - -type - AsyncGetBurnFees = ref object of QObjectTaskArg - chainId: int - contractAddress: string - amount: Uint256 - addressFrom: string - requestId: string - -proc asyncGetBurnFeesTask(argEncoded: string) {.gcsafe, nimcall.} = - let arg = decode[AsyncGetBurnFees](argEncoded) - try: - var gasTable: Table[ContractTuple, int] # gas per contract - var feeTable: Table[int, SuggestedFeesDto] # fees for chain - - let estimations = community_tokens.estimateBurn(arg.chainId, arg.contractAddress, arg.addressFrom, arg.amount).result - gasTable[(arg.chainId, arg.contractAddress)] = estimations{"gasUnits"}.getInt - feeTable[arg.chainId] = estimations{"suggestedFees"}.toSuggestedFeesDto() - - arg.finish(%* { - "feeTable": tableToJsonArray(feeTable), - "gasTable": tableToJsonArray(gasTable), - "chainId": arg.chainId, - "addressFrom": arg.addressFrom, - "error": "", - "requestId": arg.requestId - }) - except Exception as e: - arg.finish(%* { - "error": e.msg, - "requestId": arg.requestId, - }) - -type - AsyncGetMintFees = ref object of QObjectTaskArg - collectiblesAndAmounts: seq[CommunityTokenAndAmount] - walletAddresses: seq[string] - addressFrom: string - requestId: string - -proc asyncGetMintFeesTask(argEncoded: string) {.gcsafe, nimcall.} = - let arg = decode[AsyncGetMintFees](argEncoded) - try: - var gasTable: Table[ContractTuple, int] # gas per contract - var feeTable: Table[int, SuggestedFeesDto] # fees for chain - for collectibleAndAmount in arg.collectiblesAndAmounts: - # get fees if we do not have for this chain yet - let chainId = collectibleAndAmount.communityToken.chainId - # get gas for smart contract - let estimations = community_tokens.estimateMintTokens(chainId, - collectibleAndAmount.communityToken.address, arg.addressFrom, - arg.walletAddresses, collectibleAndAmount.amount).result - gasTable[(chainId, collectibleAndAmount.communityToken.address)] = estimations{"gasUnits"}.getInt - feeTable[chainId] = estimations{"suggestedFees"}.toSuggestedFeesDto() - arg.finish(%* { - "feeTable": tableToJsonArray(feeTable), - "gasTable": tableToJsonArray(gasTable), - "addressFrom": arg.addressFrom, - "error": "", - "requestId": arg.requestId - }) - except Exception as e: - let output = %* { - "error": e.msg, - "requestId": arg.requestId - } - arg.finish(output) - type FetchCollectibleOwnersArg = ref object of QObjectTaskArg chainId*: int diff --git a/src/app_service/service/community_tokens/dto/deployment_parameters.nim b/src/app_service/service/community_tokens/dto/deployment_parameters.nim index b4e78c49820..e9b1ec5faaa 100644 --- a/src/app_service/service/community_tokens/dto/deployment_parameters.nim +++ b/src/app_service/service/community_tokens/dto/deployment_parameters.nim @@ -1,6 +1,9 @@ import json, stint -import ../../../../backend/interpret/cropped_image -import ../../../common/types +import backend/interpret/cropped_image +import app_service/common/types + +include app_service/common/json_utils +from app_service/common/account_constants import ZERO_ADDRESS type DeploymentParameters* = object @@ -30,11 +33,43 @@ proc `%`*(x: DeploymentParameters): JsonNode = result["remoteSelfDestruct"] = %x.remoteSelfDestruct result["tokenUri"] = %x.tokenUri result["decimals"] = %x.decimals - result["ownerTokenAddress"] = %x.ownerTokenAddress - result["masterTokenAddress"] = %x.masterTokenAddress + var address = ZERO_ADDRESS + if x.ownerTokenAddress.len > 0: + address = x.ownerTokenAddress + result["ownerTokenAddress"] = %address + address = ZERO_ADDRESS + if x.masterTokenAddress.len > 0: + address = x.masterTokenAddress + result["masterTokenAddress"] = %address result["description"] = %x.description result["communityId"] = %x.communityId if x.croppedImageJson != "": result["croppedImage"] = %newCroppedImage(x.croppedImageJson) result["base64image"] = %x.base64image - result["tokenType"] = %x.tokenType \ No newline at end of file + result["tokenType"] = %x.tokenType + +proc toDeploymentParameters*(jsonObj: JsonNode): DeploymentParameters = + result = DeploymentParameters() + discard jsonObj.getProp("name", result.name) + discard jsonObj.getProp("symbol", result.symbol) + discard jsonObj.getProp("infiniteSupply", result.infiniteSupply) + discard jsonObj.getProp("transferable", result.transferable) + discard jsonObj.getProp("remoteSelfDestruct", result.remoteSelfDestruct) + discard jsonObj.getProp("tokenUri", result.tokenUri) + discard jsonObj.getProp("decimals", result.decimals) + discard jsonObj.getProp("ownerTokenAddress", result.ownerTokenAddress) + discard jsonObj.getProp("masterTokenAddress", result.masterTokenAddress) + discard jsonObj.getProp("description", result.description) + discard jsonObj.getProp("communityId", result.communityId) + discard jsonObj.getProp("base64image", result.base64image) + var tmpObj: JsonNode + if jsonObj.getProp("supply", tmpObj): + result.supply = stint.fromHex(UInt256, tmpObj.getStr) + if jsonObj.getProp("croppedImage", tmpObj): + result.croppedImageJson = tmpObj.getStr + if jsonObj.getProp("tokenType", tmpObj): + let txType = tmpObj.getInt + if txType < ord(low(TokenType)) or txType >= ord(high(TokenType)): + result.tokenType = TokenType.Native + else: + result.tokenType = TokenType(txType) \ No newline at end of file diff --git a/src/app_service/service/community_tokens/service.nim b/src/app_service/service/community_tokens/service.nim index 42162376b5a..5cd16c91caf 100644 --- a/src/app_service/service/community_tokens/service.nim +++ b/src/app_service/service/community_tokens/service.nim @@ -1,34 +1,39 @@ import NimQml, Tables, chronicles, json, stint, strutils, sugar, sequtils, stew/shims/strformat, times -import ../../../app/global/global_singleton -import ../../../app/core/eventemitter -import ../../../app/core/tasks/[qt, threadpool] -import ../../../app/core/signals/types - -import ../../../app/modules/shared_models/currency_amount - -import ../../../backend/collectibles as collectibles_backend -import ../../../backend/communities as communities_backend -import ../../../backend/community_tokens as tokens_backend -import ../transaction/service as transaction_service -import ../token/service as token_service -import ../settings/service as settings_service -import ../wallet_account/service as wallet_account_service -import ../activity_center/service as ac_service -import ../community/service as community_service -import app_service/service/currency/service as currency_service -import ../ens/utils as ens_utils -import ../eth/dto/transaction + +import app/global/global_singleton +import app/core/eventemitter +import app/core/tasks/[qt, threadpool] +import app/core/signals/types + +import app/modules/shared_models/currency_amount + +import backend/backend +import backend/response_type +import backend/wallet +import backend/collectibles as collectibles_backend +import backend/communities as communities_backend +import backend/community_tokens as tokens_backend from backend/collectibles_types import CollectibleOwner -import ../../../backend/backend -import ../../../backend/response_type +import app_service/service/network/service as network_service +import app_service/service/transaction/service as transaction_service +import app_service/service/token/service as token_service +import app_service/service/settings/service as settings_service +import app_service/service/wallet_account/service as wallet_account_service +import app_service/service/activity_center/service as ac_service +import app_service/service/community/service as community_service +import app_service/service/currency/service as currency_service +import app_service/service/ens/utils as ens_utils +import app_service/service/eth/utils as eth_utils +import app_service/service/eth/dto/transaction +import app_service/service/community/dto/community +import app_service/service/contacts/dto/contacts +import app_service/common/activity_center +import app_service/common/conversion +import app_service/common/types +import app_service/common/account_constants +import app_service/common/utils as common_utils -import ../../common/activity_center -import ../../common/conversion -import ../../common/account_constants -import ../../common/utils as common_utils -import ../community/dto/community -import ../contacts/dto/contacts import ./community_collectible_owner import ./dto/deployment_parameters @@ -39,10 +44,10 @@ export community_token export deployment_parameters export community_token_owner -const ethSymbol = "ETH" - include async_tasks +const ethSymbol = "ETH" + logScope: topics = "community-tokens-service" @@ -71,12 +76,13 @@ type CommunityTokenDeploymentArgs* = ref object of Args communityToken*: CommunityTokenDto transactionHash*: string + error*: string type OwnerTokenDeploymentArgs* = ref object of Args ownerToken*: CommunityTokenDto masterToken*: CommunityTokenDto - transactionHash*: string + error*: string type CommunityTokenRemovedArgs* = ref object of Args @@ -253,8 +259,7 @@ proc toContractDetails*(jsonObj: JsonNode): ContractDetails = # Signals which may be emitted by this service: const SIGNAL_COMMUNITY_TOKEN_DEPLOY_STATUS* = "communityTokens-communityTokenDeployStatus" -const SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STARTED* = "communityTokens-communityTokenDeploymentStarted" -const SIGNAL_COMPUTE_DEPLOY_FEE* = "communityTokens-computeDeployFee" +const SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STORED* = "communityTokens-communityTokenDeploymentStored" const SIGNAL_COMPUTE_SET_SIGNER_FEE* = "communityTokens-computeSetSignerFee" const SIGNAL_COMPUTE_SELF_DESTRUCT_FEE* = "communityTokens-computeSelfDestructFee" const SIGNAL_COMPUTE_BURN_FEE* = "communityTokens-computeBurnFee" @@ -268,7 +273,7 @@ const SIGNAL_AIRDROP_STATUS* = "communityTokens-airdropStatus" const SIGNAL_REMOVE_COMMUNITY_TOKEN_FAILED* = "communityTokens-removeCommunityTokenFailed" const SIGNAL_COMMUNITY_TOKEN_REMOVED* = "communityTokens-communityTokenRemoved" const SIGNAL_OWNER_TOKEN_DEPLOY_STATUS* = "communityTokens-ownerTokenDeployStatus" -const SIGNAL_OWNER_TOKEN_DEPLOYMENT_STARTED* = "communityTokens-ownerTokenDeploymentStarted" +const SIGNAL_OWNER_TOKEN_DEPLOYMENT_STORED* = "communityTokens-ownerTokenDeploymentStored" const SIGNAL_COMMUNITY_TOKENS_DETAILS_LOADED* = "communityTokens-communityTokenDetailsLoaded" const SIGNAL_OWNER_TOKEN_RECEIVED* = "communityTokens-ownerTokenReceived" const SIGNAL_SET_SIGNER_STATUS* = "communityTokens-setSignerStatus" @@ -281,6 +286,7 @@ QtObject: Service* = ref object of QObject events: EventEmitter threadpool: ThreadPool + networkService: network_service.Service transactionService: transaction_service.Service tokenService: token_service.Service settingsService: settings_service.Service @@ -322,6 +328,7 @@ QtObject: proc newService*( events: EventEmitter, threadpool: ThreadPool, + networkService: network_service.Service, transactionService: transaction_service.Service, tokenService: token_service.Service, settingsService: settings_service.Service, @@ -334,6 +341,7 @@ QtObject: result.QObject.setup result.events = events result.threadpool = threadpool + result.networkService = networkService result.transactionService = transactionService result.tokenService = tokenService result.settingsService = settingsService @@ -357,6 +365,11 @@ QtObject: discard tokens_backend.removeCommunityToken(chainId, contractAddress) self.communityTokensCache = self.communityTokensCache.filter(x => ((x.chainId != chainId) or (x.address != contractAddress))) + proc getCommunityTokenFromCache*(self: Service, chainId: int, address: string): CommunityTokenDto = + for token in self.communityTokensCache: + if token.chainId == chainId and cmpIgnoreCase(token.address, address) == 0: + return token + # end of cache functions proc processReceivedCollectiblesWalletEvent(self: Service, jsonMessage: string, accounts: seq[string]) = @@ -604,17 +617,17 @@ QtObject: if receivedData.errorString != "": error "Community token transaction has finished but the system error occured. Probably state of the token in database is broken.", errorString=receivedData.errorString, transactionHash=receivedData.hash, transactionSuccess=receivedData.success - if receivedData.transactionType == $PendingTransactionTypeDto.SetSignerPublicKey: + if receivedData.sendType == int(SendType.CommunitySetSignerPubKey): self.processSetSignerTransactionEvent(receivedData) - elif receivedData.transactionType == $PendingTransactionTypeDto.AirdropCommunityToken: + elif receivedData.sendType == int(SendType.CommunityMintTokens): self.processAirdropTransactionEvent(receivedData) - elif receivedData.transactionType == $PendingTransactionTypeDto.RemoteDestructCollectible: + elif receivedData.sendType == int(SendType.CommunityRemoteBurn): self.processRemoteDestructEvent(receivedData) - elif receivedData.transactionType == $PendingTransactionTypeDto.BurnCommunityToken: + elif receivedData.sendType == int(SendType.CommunityBurn): self.processBurnEvent(receivedData) - elif receivedData.transactionType == $PendingTransactionTypeDto.DeployCommunityToken: + elif receivedData.sendType == int(SendType.CommunityDeployAssets) or receivedData.sendType == int(SendType.CommunityDeployCollectibles): self.processDeployCommunityToken(receivedData) - elif receivedData.transactionType == $PendingTransactionTypeDto.DeployOwnerToken: + elif receivedData.sendType == int(SendType.CommunityDeployOwnerToken): self.processDeployOwnerToken(receivedData) proc buildTransactionDataDto(self: Service, addressFrom: string, chainId: int, contractAddress: string): TransactionDataDto = @@ -631,68 +644,62 @@ QtObject: proc temporaryOwnerContractAddress*(ownerContractTransactionHash: string): string = return ownerContractTransactionHash & "-owner" - proc deployOwnerContracts*(self: Service, communityId: string, addressFrom: string, password: string, - ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters, chainId: int) = + proc storeDeployedOwnerContract*(self: Service, addressFrom: string, chainId: int, txHash: string, + ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters) = + var data = OwnerTokenDeploymentArgs( + ownerToken: CommunityTokenDto( + communityId: ownerDeploymentParams.communityId, + name: ownerDeploymentParams.name, + symbol: ownerDeploymentParams.symbol, + tokenType: ownerDeploymentParams.tokenType, + ), + masterToken: CommunityTokenDto( + communityId: masterDeploymentParams.communityId, + name: masterDeploymentParams.name, + symbol: masterDeploymentParams.symbol, + tokenType: masterDeploymentParams.tokenType, + ), + ) try: - let txData = self.buildTransactionDataDto(addressFrom, chainId, "") - if txData.source == parseAddress(ZERO_ADDRESS): - return - # set my pub key as signer - let signerPubKey = singletonInstance.userProfile.getPubKey() + let response = tokens_backend.storeDeployedOwnerToken(addressFrom, chainId, txHash, %ownerDeploymentParams, %masterDeploymentParams) + if not response.error.isNil: + raise newException(CatchableError, response.error.message) - # deploy contract - let response = tokens_backend.deployOwnerToken(chainId, %ownerDeploymentParams, %masterDeploymentParams, - signerPubKey, %txData, common_utils.hashPassword(password)) - let transactionHash = response.result["transactionHash"].getStr() let deployedOwnerToken = toCommunityTokenDto(response.result["ownerToken"]) let deployedMasterToken = toCommunityTokenDto(response.result["masterToken"]) - debug "Deployment transaction hash ", transactionHash=transactionHash - self.communityTokensCache.add(deployedOwnerToken) self.communityTokensCache.add(deployedMasterToken) - - let data = OwnerTokenDeploymentArgs(ownerToken: deployedOwnerToken, masterToken: deployedMasterToken, transactionHash: transactionHash) - self.events.emit(SIGNAL_OWNER_TOKEN_DEPLOYMENT_STARTED, data) - - except RpcException: - error "Error deploying owner contract", message = getCurrentExceptionMsg() - let data = OwnerTokenDeployedStatusArgs(communityId: communityId, - deployState: DeployState.Failed) - self.events.emit(SIGNAL_OWNER_TOKEN_DEPLOY_STATUS, data) - - proc deployContract*(self: Service, communityId: string, addressFrom: string, password: string, deploymentParams: DeploymentParameters, chainId: int) = + data.ownerToken = deployedOwnerToken + data.masterToken = deployedMasterToken + except Exception as e: + data.error = e.msg + error "Error storing deployed owner contract", msg = e.msg + self.events.emit(SIGNAL_OWNER_TOKEN_DEPLOYMENT_STORED, data) + + proc storeDeployedContract*(self: Service, sendType: SendType, addressFrom: string, addressTo: string, chainId: int, + txHash: string, deploymentParams: DeploymentParameters) = + var data = CommunityTokenDeploymentArgs( + transactionHash: txHash + ) try: - let txData = self.buildTransactionDataDto(addressFrom, chainId, "") - if txData.source == parseAddress(ZERO_ADDRESS): - return - var response: RpcResponse[JsonNode] - case deploymentParams.tokenType - of TokenType.ERC721: - response = tokens_backend.deployCollectibles(chainId, %deploymentParams, %txData, common_utils.hashPassword(password)) - of TokenType.ERC20: - response = tokens_backend.deployAssets(chainId, %deploymentParams, %txData, common_utils.hashPassword(password)) + case sendType + of SendType.CommunityDeployAssets: + response = tokens_backend.storeDeployedAssets(addressFrom, addressTo, chainId, txHash, %deploymentParams) + of SendType.CommunityDeployCollectibles: + response = tokens_backend.storeDeployedCollectibles(addressFrom, addressTo, chainId, txHash, %deploymentParams) else: - error "Contract deployment error - unknown token type", tokenType=deploymentParams.tokenType - return + let err = "unexpected send type " & $sendType + raise newException(CatchableError, err) - let contractAddress = response.result["contractAddress"].getStr() - let transactionHash = response.result["transactionHash"].getStr() let deployedCommunityToken = toCommunityTokenDto(response.result["communityToken"]) - debug "Deployed contract address ", contractAddress=contractAddress - debug "Deployment transaction hash ", transactionHash=transactionHash - - # add to cache self.communityTokensCache.add(deployedCommunityToken) - let data = CommunityTokenDeploymentArgs(communityToken: deployedCommunityToken, transactionHash: transactionHash) - self.events.emit(SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STARTED, data) - - except RpcException: - error "Error deploying contract", message = getCurrentExceptionMsg() - let data = CommunityTokenDeployedStatusArgs(communityId: communityId, - deployState: DeployState.Failed) - self.events.emit(SIGNAL_COMMUNITY_TOKEN_DEPLOY_STATUS, data) + data.communityToken = deployedCommunityToken + except Exception as e: + data.error = e.msg + error "Error storing deployed contract", msg = e.msg + self.events.emit(SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STORED, data) proc getCommunityTokens*(self: Service, communityId: string): seq[CommunityTokenDto] = return self.communityTokensCache.filter(x => (x.communityId == communityId)) @@ -833,40 +840,58 @@ QtObject: except RpcException: error "Error getting remote destructed amount", message = getCurrentExceptionMsg() - proc airdropTokens*(self: Service, communityId: string, password: string, collectiblesAndAmounts: seq[CommunityTokenAndAmount], walletAddresses: seq[string], addressFrom: string) = + proc computeAirdropFee*(self: Service, uuid: string, collectiblesAndAmounts: seq[CommunityTokenAndAmount], walletAddresses: seq[string], addressFrom: string) = + let sendType = SendType.CommunityMintTokens try: + if collectiblesAndAmounts.len == 0: + raise newException(CatchableError, "no collectibles to airdrop") + let chainId = collectiblesAndAmounts[0].communityToken.chainId + let communityId = collectiblesAndAmounts[0].communityToken.communityId + var transferDetails: seq[JsonNode] for collectibleAndAmount in collectiblesAndAmounts: - let txData = self.buildTransactionDataDto(addressFrom, collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address) - if txData.source == parseAddress(ZERO_ADDRESS): - return - debug "Airdrop tokens ", chainId=collectibleAndAmount.communityToken.chainId, address=collectibleAndAmount.communityToken.address, amount=collectibleAndAmount.amount - let response = tokens_backend.mintTokens(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address, %txData, common_utils.hashPassword(password), walletAddresses, collectibleAndAmount.amount) - let transactionHash = response.result.getStr() - debug "Airdrop transaction hash ", transactionHash=transactionHash - - var data = AirdropArgs(communityToken: collectibleAndAmount.communityToken, transactionHash: transactionHash, status: ContractTransactionStatus.InProgress) - self.events.emit(SIGNAL_AIRDROP_STATUS, data) - except RpcException: - error "Error airdropping tokens", message = getCurrentExceptionMsg() - - proc computeAirdropFee*(self: Service, collectiblesAndAmounts: seq[CommunityTokenAndAmount], walletAddresses: seq[string], addressFrom: string, requestId: string) = - try: - self.tempTokensAndAmounts = collectiblesAndAmounts - let arg = AsyncGetMintFees( - tptr: asyncGetMintFeesTask, - vptr: cast[ByteAddress](self.vptr), - slot: "onAirdropFees", - collectiblesAndAmounts: collectiblesAndAmounts, - walletAddresses: walletAddresses, - addressFrom: addressFrom, - requestId: requestId + let amountHex = "0x" & eth_utils.stripLeadingZeros(collectibleAndAmount.amount.toHex) + transferDetails.add(%* { + "tokenContractAddress": collectibleAndAmount.communityToken.address, + "amount": amountHex, + }) + self.transactionService.suggestedCommunityRoutes( + uuid, + sendType, + chainId, + addressFrom, + communityId, + signerPubKey = "", + walletAddresses, + transferDetails ) - self.threadpool.start(arg) except Exception as e: - error "Error loading airdrop fees", msg = e.msg - var dataToEmit = AirdropFeesArgs() - dataToEmit.errorCode = ComputeFeeErrorCode.Other - self.events.emit(SIGNAL_COMPUTE_AIRDROP_FEE, dataToEmit) + error "Error loading deploy owner fees", msg = e.msg + self.transactionService.emitSuggestedRoutesReadySignal( + SuggestedRoutesArgs( + uuid: uuid, + sendType: sendType, + errCode: $InternalErrorCode, + errDescription: e.msg + ) + ) + + # try: + # self.tempTokensAndAmounts = collectiblesAndAmounts + # let arg = AsyncGetMintFees( + # tptr: asyncGetMintFeesTask, + # vptr: cast[ByteAddress](self.vptr), + # slot: "onAirdropFees", + # collectiblesAndAmounts: collectiblesAndAmounts, + # walletAddresses: walletAddresses, + # addressFrom: addressFrom, + # requestId: requestId + # ) + # self.threadpool.start(arg) + # except Exception as e: + # error "Error loading airdrop fees", msg = e.msg + # var dataToEmit = AirdropFeesArgs() + # dataToEmit.errorCode = ComputeFeeErrorCode.Other + # self.events.emit(SIGNAL_COMPUTE_AIRDROP_FEE, dataToEmit) proc getFiatValue(self: Service, cryptoBalance: float, cryptoSymbol: string): float = if (cryptoSymbol == ""): @@ -880,61 +905,88 @@ QtObject: if common_utils.contractUniqueKey(token.chainId, token.address) == contractUniqueKey: return token - proc computeDeployFee*(self: Service, chainId: int, accountAddress: string, tokenType: TokenType, requestId: string) = + proc computeDeployTokenFee*(self: Service, uuid: string, chainId: int, accountFrom: string, communityId: string, deploymentParams: DeploymentParameters) = + var sendType = SendType.CommunityDeployAssets + if deploymentParams.tokenType == TokenType.ERC721: + sendType = SendType.CommunityDeployCollectibles try: - if tokenType != TokenType.ERC20 and tokenType != TokenType.ERC721: - error "Error loading fees: unknown token type", tokenType = tokenType - return - let arg = AsyncGetDeployFeesArg( - tptr: asyncGetDeployFeesTask, - vptr: cast[ByteAddress](self.vptr), - slot: "onDeployFees", - chainId: chainId, - addressFrom: accountAddress, - tokenType: tokenType, - requestId: requestId + self.transactionService.suggestedCommunityRoutes( + uuid, + sendType, + chainId, + accountFrom, + communityId, + signerPubKey = "", + walletAddresses = @[], + transferDetails = @[], + signature = "", + ownerTokenParameters = JsonNode(), + masterTokenParameters = JsonNode(), + %deploymentParams ) - self.threadpool.start(arg) except Exception as e: - #TODO: handle error - emit error signal - error "Error loading fees", msg = e.msg - - proc computeSetSignerFee*(self: Service, chainId: int, contractAddress: string, accountAddress: string, requestId: string) = - try: - let arg = AsyncSetSignerFeesArg( - tptr: asyncSetSignerFeesTask, - vptr: cast[ByteAddress](self.vptr), - slot: "onSetSignerFees", - chainId: chainId, - contractAddress: contractAddress, - addressFrom: accountAddress, - requestId: requestId, - newSignerPubKey: singletonInstance.userProfile.getPubKey() + error "Error loading deploy owner fees", msg = e.msg + self.transactionService.emitSuggestedRoutesReadySignal( + SuggestedRoutesArgs( + uuid: uuid, + sendType: sendType, + errCode: $InternalErrorCode, + errDescription: e.msg + ) ) - self.threadpool.start(arg) - except Exception as e: - #TODO: handle error - emit error signal - error "Error loading fees", msg = e.msg - proc computeDeployOwnerContractsFee*(self: Service, chainId: int, accountAddress: string, communityId: string, ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters, requestId: string) = + proc computeSetSignerFee*(self: Service, chainId: int, contractAddress: string, accountAddress: string, requestId: string) = + discard + # try: + # let arg = AsyncSetSignerFeesArg( + # tptr: asyncSetSignerFeesTask, + # vptr: cast[ByteAddress](self.vptr), + # slot: "onSetSignerFees", + # chainId: chainId, + # contractAddress: contractAddress, + # addressFrom: accountAddress, + # requestId: requestId, + # newSignerPubKey: singletonInstance.userProfile.getPubKey() + # ) + # self.threadpool.start(arg) + # except Exception as e: + # #TODO: handle error - emit error signal + # error "Error loading fees", msg = e.msg + + + proc computeDeployOwnerContractsFee*(self: Service, uuid: string, chainId: int, accountFrom: string, communityId: string, + ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters) = try: - let arg = AsyncDeployOwnerContractsFeesArg( - tptr: asyncGetDeployOwnerContractsFeesTask, - vptr: cast[ByteAddress](self.vptr), - slot: "onDeployOwnerContractsFees", - chainId: chainId, - addressFrom: accountAddress, - requestId: requestId, - signerPubKey: singletonInstance.userProfile.getPubKey(), - communityId: communityId, - ownerParams: %ownerDeploymentParams, - masterParams: %masterDeploymentParams + var signatureResult: JsonNode + let err = tokens_backend.createCommunityTokenDeploymentSignature(signatureResult, chainId, accountFrom, communityId) + if err.len > 0: + raise newException(CatchableError, "createCommunityTokenDeploymentSignature failed " & err) + let signature = signatureResult.getStr + + self.transactionService.suggestedCommunityRoutes( + uuid, + SendType.CommunityDeployOwnerToken, + chainId, + accountFrom, + communityId, + signerPubKey = singletonInstance.userProfile.getPubKey(), + walletAddresses = @[], + transferDetails = @[], + signature, + %ownerDeploymentParams, + %masterDeploymentParams ) - self.threadpool.start(arg) except Exception as e: - #TODO: handle error - emit error signal - error "Error loading fees", msg = e.msg + error "Error loading deploy owner fees", msg = e.msg + self.transactionService.emitSuggestedRoutesReadySignal( + SuggestedRoutesArgs( + uuid: uuid, + sendType: SendType.CommunityDeployOwnerToken, + errCode: $InternalErrorCode, + errDescription: e.msg + ) + ) proc getOwnerBalances(self: Service, contractOwners: seq[CommunityCollectibleOwner], ownerAddress: string): seq[CollectibleBalance] = for owner in contractOwners: @@ -964,47 +1016,49 @@ QtObject: return tokenIds proc selfDestructCollectibles*(self: Service, communityId: string, password: string, walletAndAmounts: seq[WalletAndAmount], contractUniqueKey: string, addressFrom: string) = - try: - let contract = self.findContractByUniqueId(contractUniqueKey) - let tokenIds = self.getTokensToBurn(walletAndAmounts, contract) - if len(tokenIds) == 0: - debug "No token ids to remote burn", walletAndAmounts=walletAndAmounts - return - var addresses: seq[string] = @[] - for walletAndAmount in walletAndAmounts: - addresses.add(walletAndAmount.walletAddress) - let txData = self.buildTransactionDataDto(addressFrom, contract.chainId, contract.address) - debug "Remote destruct collectibles ", chainId=contract.chainId, address=contract.address, tokens=tokenIds - let response = tokens_backend.remoteBurn(contract.chainId, contract.address, %txData, common_utils.hashPassword(password), - tokenIds, $(%addresses)) - let transactionHash = response.result.getStr() - debug "Remote destruct transaction hash ", transactionHash=transactionHash - - var data = RemoteDestructArgs(communityToken: contract, transactionHash: transactionHash, status: ContractTransactionStatus.InProgress, remoteDestructAddresses: addresses) - self.events.emit(SIGNAL_REMOTE_DESTRUCT_STATUS, data) - except Exception as e: - error "Remote self destruct error", msg = e.msg + discard + # try: + # let contract = self.findContractByUniqueId(contractUniqueKey) + # let tokenIds = self.getTokensToBurn(walletAndAmounts, contract) + # if len(tokenIds) == 0: + # debug "No token ids to remote burn", walletAndAmounts=walletAndAmounts + # return + # var addresses: seq[string] = @[] + # for walletAndAmount in walletAndAmounts: + # addresses.add(walletAndAmount.walletAddress) + # let txData = self.buildTransactionDataDto(addressFrom, contract.chainId, contract.address) + # debug "Remote destruct collectibles ", chainId=contract.chainId, address=contract.address, tokens=tokenIds + # let response = tokens_backend.remoteBurn(contract.chainId, contract.address, %txData, common_utils.hashPassword(password), + # tokenIds, $(%addresses)) + # let transactionHash = response.result.getStr() + # debug "Remote destruct transaction hash ", transactionHash=transactionHash + + # var data = RemoteDestructArgs(communityToken: contract, transactionHash: transactionHash, status: ContractTransactionStatus.InProgress, remoteDestructAddresses: addresses) + # self.events.emit(SIGNAL_REMOTE_DESTRUCT_STATUS, data) + # except Exception as e: + # error "Remote self destruct error", msg = e.msg proc computeSelfDestructFee*(self: Service, walletAndAmountList: seq[WalletAndAmount], contractUniqueKey: string, addressFrom: string, requestId: string) = - try: - let contract = self.findContractByUniqueId(contractUniqueKey) - let tokenIds = self.getTokensToBurn(walletAndAmountList, contract) - if len(tokenIds) == 0: - warn "token list is empty" - return - let arg = AsyncGetRemoteBurnFees( - tptr: asyncGetRemoteBurnFeesTask, - vptr: cast[ByteAddress](self.vptr), - slot: "onSelfDestructFees", - chainId: contract.chainId, - contractAddress: contract.address, - tokenIds: tokenIds, - addressFrom: addressFrom, - requestId: requestId - ) - self.threadpool.start(arg) - except Exception as e: - error "Error loading fees", msg = e.msg + discard + # try: + # let contract = self.findContractByUniqueId(contractUniqueKey) + # let tokenIds = self.getTokensToBurn(walletAndAmountList, contract) + # if len(tokenIds) == 0: + # warn "token list is empty" + # return + # let arg = AsyncGetRemoteBurnFees( + # tptr: asyncGetRemoteBurnFeesTask, + # vptr: cast[ByteAddress](self.vptr), + # slot: "onSelfDestructFees", + # chainId: contract.chainId, + # contractAddress: contract.address, + # tokenIds: tokenIds, + # addressFrom: addressFrom, + # requestId: requestId + # ) + # self.threadpool.start(arg) + # except Exception as e: + # error "Error loading fees", msg = e.msg proc create0CurrencyAmounts(self: Service): (CurrencyAmount, CurrencyAmount) = let ethCurrency = newCurrencyAmount(0.0, ethSymbol, 1, false) @@ -1025,69 +1079,72 @@ QtObject: return errorCode proc burnTokens*(self: Service, communityId: string, password: string, contractUniqueKey: string, amount: Uint256, addressFrom: string) = - try: - var contract = self.findContractByUniqueId(contractUniqueKey) - let txData = self.buildTransactionDataDto(addressFrom, contract.chainId, contract.address) - debug "Burn tokens ", chainId=contract.chainId, address=contract.address, amount=amount - let response = tokens_backend.burn(contract.chainId, contract.address, %txData, common_utils.hashPassword(password), amount) - let transactionHash = response.result.getStr() - debug "Burn transaction hash ", transactionHash=transactionHash - - var data = RemoteDestructArgs(communityToken: contract, transactionHash: transactionHash, status: ContractTransactionStatus.InProgress) - self.events.emit(SIGNAL_BURN_STATUS, data) - except Exception as e: - error "Burn error", msg = e.msg + discard + # try: + # var contract = self.findContractByUniqueId(contractUniqueKey) + # let txData = self.buildTransactionDataDto(addressFrom, contract.chainId, contract.address) + # debug "Burn tokens ", chainId=contract.chainId, address=contract.address, amount=amount + # let response = tokens_backend.burn(contract.chainId, contract.address, %txData, common_utils.hashPassword(password), amount) + # let transactionHash = response.result.getStr() + # debug "Burn transaction hash ", transactionHash=transactionHash + + # var data = RemoteDestructArgs(communityToken: contract, transactionHash: transactionHash, status: ContractTransactionStatus.InProgress) + # self.events.emit(SIGNAL_BURN_STATUS, data) + # except Exception as e: + # error "Burn error", msg = e.msg proc setSigner*(self: Service, password: string, communityId: string, chainId: int, contractAddress: string, addressFrom: string) = - try: - let txData = self.buildTransactionDataDto(addressFrom, chainId, contractAddress) - debug "Set signer ", chainId=chainId, address=contractAddress - let signerPubKey = singletonInstance.userProfile.getPubKey() - let response = tokens_backend.setSignerPubKey(chainId, contractAddress, %txData, signerPubKey, common_utils.hashPassword(password)) - let transactionHash = response.result.getStr() - debug "Set signer transaction hash ", transactionHash=transactionHash - - let data = SetSignerArgs(status: ContractTransactionStatus.InProgress, - chainId: chainId, - transactionHash: transactionHash, - communityId: communityId) - - self.events.emit(SIGNAL_SET_SIGNER_STATUS, data) - - # observe transaction state - let contractDetails = ContractDetails(chainId: chainId, contractAddress: contractAddress, communityId: communityId) - self.transactionService.watchTransaction( - transactionHash, - addressFrom, - contractAddress, - $PendingTransactionTypeDto.SetSignerPublicKey, - $(%contractDetails), - chainId, - ) - except Exception as e: - error "Set signer error", msg = e.msg + discard + # try: + # let txData = self.buildTransactionDataDto(addressFrom, chainId, contractAddress) + # debug "Set signer ", chainId=chainId, address=contractAddress + # let signerPubKey = singletonInstance.userProfile.getPubKey() + # let response = tokens_backend.setSignerPubKey(chainId, contractAddress, %txData, signerPubKey, common_utils.hashPassword(password)) + # let transactionHash = response.result.getStr() + # debug "Set signer transaction hash ", transactionHash=transactionHash + + # let data = SetSignerArgs(status: ContractTransactionStatus.InProgress, + # chainId: chainId, + # transactionHash: transactionHash, + # communityId: communityId) + + # self.events.emit(SIGNAL_SET_SIGNER_STATUS, data) + + # # observe transaction state + # let contractDetails = ContractDetails(chainId: chainId, contractAddress: contractAddress, communityId: communityId) + # self.transactionService.watchTransaction( + # transactionHash, + # addressFrom, + # contractAddress, + # $PendingTransactionTypeDto.SetSignerPublicKey, + # $(%contractDetails), + # chainId, + # ) + # except Exception as e: + # error "Set signer error", msg = e.msg proc computeBurnFee*(self: Service, contractUniqueKey: string, amount: Uint256, addressFrom: string, requestId: string) = - try: - let contract = self.findContractByUniqueId(contractUniqueKey) - let arg = AsyncGetBurnFees( - tptr: asyncGetBurnFeesTask, - vptr: cast[ByteAddress](self.vptr), - slot: "onBurnFees", - chainId: contract.chainId, - contractAddress: contract.address, - amount: amount, - addressFrom: addressFrom, - requestId: requestId - ) - self.threadpool.start(arg) - except Exception as e: - error "Error loading burn fees", msg = e.msg - - proc createComputeFeeArgsWithError(self:Service, errorMessage: string): ComputeFeeArgs = - let errorCode = self.getErrorCodeFromMessage(errorMessage) - let (ethCurrency, fiatCurrency) = self.create0CurrencyAmounts() - return ComputeFeeArgs(ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, errorCode: errorCode) + discard + # try: + # let contract = self.findContractByUniqueId(contractUniqueKey) + # let arg = AsyncGetBurnFees( + # tptr: asyncGetBurnFeesTask, + # vptr: cast[ByteAddress](self.vptr), + # slot: "onBurnFees", + # chainId: contract.chainId, + # contractAddress: contract.address, + # amount: amount, + # addressFrom: addressFrom, + # requestId: requestId + # ) + # self.threadpool.start(arg) + # except Exception as e: + # error "Error loading burn fees", msg = e.msg + + # proc createComputeFeeArgsWithError(self:Service, errorMessage: string): ComputeFeeArgs = + # let errorCode = self.getErrorCodeFromMessage(errorMessage) + # let (ethCurrency, fiatCurrency) = self.create0CurrencyAmounts() + # return ComputeFeeArgs(ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, errorCode: errorCode) # Returns eth value with l1 fee included proc computeEthValue(self:Service, gasUnits: int, suggestedFees: SuggestedFeesDto): float = @@ -1114,17 +1171,17 @@ QtObject: balance += self.currencyService.parseCurrencyValueByTokensKey(token.tokensKey, b) return balance - proc createComputeFeeArgsFromEthAndBalance(self: Service, ethValue: float, balance: float): ComputeFeeArgs = - let fiatValue = self.getFiatValue(ethValue, ethSymbol) - let (ethCurrency, fiatCurrency) = self.createCurrencyAmounts(ethValue, fiatValue) - return ComputeFeeArgs(ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, - errorCode: (if ethValue > balance: ComputeFeeErrorCode.Balance else: ComputeFeeErrorCode.Success)) + # proc createComputeFeeArgsFromEthAndBalance(self: Service, ethValue: float, balance: float): ComputeFeeArgs = + # let fiatValue = self.getFiatValue(ethValue, ethSymbol) + # let (ethCurrency, fiatCurrency) = self.createCurrencyAmounts(ethValue, fiatValue) + # return ComputeFeeArgs(ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, + # errorCode: (if ethValue > balance: ComputeFeeErrorCode.Balance else: ComputeFeeErrorCode.Success)) - proc createComputeFeeArgs(self: Service, gasUnits: int, suggestedFees: SuggestedFeesDto, chainId: int, walletAddress: string): ComputeFeeArgs = - let ethValue = self.computeEthValue(gasUnits, suggestedFees) - let balance = self.getWalletBalanceForChain(walletAddress, chainId) - debug "computing fees", walletBalance=balance, ethValueWithL1Fee=ethValue, l1Fee=gwei2Eth(suggestedFees.l1GasFee) - return self.createComputeFeeArgsFromEthAndBalance(ethValue, balance) + # proc createComputeFeeArgs(self: Service, gasUnits: int, suggestedFees: SuggestedFeesDto, chainId: int, walletAddress: string): ComputeFeeArgs = + # let ethValue = self.computeEthValue(gasUnits, suggestedFees) + # let balance = self.getWalletBalanceForChain(walletAddress, chainId) + # debug "computing fees", walletBalance=balance, ethValueWithL1Fee=ethValue, l1Fee=gwei2Eth(suggestedFees.l1GasFee) + # return self.createComputeFeeArgsFromEthAndBalance(ethValue, balance) # convert json returned from async task into gas table proc toGasTable(json: JsonNode): Table[ContractTuple, int] = @@ -1146,117 +1203,117 @@ QtObject: except Exception: error "Error converting to fee table", message = getCurrentExceptionMsg() - proc parseFeeResponseAndEmitSignal(self:Service, response: string, signalName: string) = - let responseJson = response.parseJson() - try: - let errorMessage = responseJson{"error"}.getStr - if errorMessage != "": - let data = self.createComputeFeeArgsWithError(errorMessage) - data.requestId = responseJson{"requestId"}.getStr - self.events.emit(signalName, data) - return - let gasTable = responseJson{"gasTable"}.toGasTable - let feeTable = responseJson{"feeTable"}.toFeeTable - let chainId = responseJson{"chainId"}.getInt - let addressFrom = responseJson{"addressFrom"}.getStr - self.tempGasTable = gasTable - self.tempFeeTable = feeTable - let gasUnits = toSeq(gasTable.values())[0] - let suggestedFees = toSeq(feeTable.values())[0] - let data = self.createComputeFeeArgs(gasUnits, suggestedFees, chainId, addressFrom) - data.requestId = responseJson{"requestId"}.getStr - self.events.emit(signalName, data) - except Exception: - error "Error creating fee args", message = getCurrentExceptionMsg() - let data = self.createComputeFeeArgsWithError(getCurrentExceptionMsg()) - data.requestId = responseJson{"requestId"}.getStr - self.events.emit(signalName, data) - - proc onDeployOwnerContractsFees*(self:Service, response: string) {.slot.} = - self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_DEPLOY_FEE) - - proc onSelfDestructFees*(self:Service, response: string) {.slot.} = - self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_SELF_DESTRUCT_FEE) - - proc onBurnFees*(self:Service, response: string) {.slot.} = - self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_BURN_FEE) - - proc onDeployFees*(self:Service, response: string) {.slot.} = - self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_DEPLOY_FEE) - - proc onSetSignerFees*(self: Service, response: string) {.slot.} = - self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_SET_SIGNER_FEE) - - proc onAirdropFees*(self:Service, response: string) {.slot.} = - var wholeEthCostForChainWallet: Table[ChainWalletTuple, float] - var ethValuesForContracts: Table[ContractTuple, float] - var allComputeFeeArgs: seq[ComputeFeeArgs] - var dataToEmit = AirdropFeesArgs() - dataToEmit.errorCode = ComputeFeeErrorCode.Success - let responseJson = response.parseJson() - - try: - let errorMessage = responseJson{"error"}.getStr - let requestId = responseJson{"requestId"}.getStr - if errorMessage != "": - for collectibleAndAmount in self.tempTokensAndAmounts: - let args = self.createComputeFeeArgsWithError(errorMessage) - args.contractUniqueKey = common_utils.contractUniqueKey(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address) - dataToEmit.fees.add(args) - let (ethTotal, fiatTotal) = self.create0CurrencyAmounts() - dataToEmit.totalEthFee = ethTotal - dataToEmit.totalFiatFee = fiatTotal - dataToEmit.errorCode = self.getErrorCodeFromMessage(errorMessage) - dataToEmit.requestId = requestId - self.events.emit(SIGNAL_COMPUTE_AIRDROP_FEE, dataToEmit) - return - - let gasTable = responseJson{"gasTable"}.toGasTable - let feeTable = responseJson{"feeTable"}.toFeeTable - let addressFrom = responseJson{"addressFrom"}.getStr - self.tempGasTable = gasTable - self.tempFeeTable = feeTable - - # compute eth cost for every contract - # also sum all eth costs per (chain, wallet) - it will be needed to compare with (chain, wallet) balance - for collectibleAndAmount in self.tempTokensAndAmounts: - let gasUnits = self.tempGasTable[(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address)] - let suggestedFees = self.tempFeeTable[collectibleAndAmount.communityToken.chainId] - let ethValue = self.computeEthValue(gasUnits, suggestedFees) - wholeEthCostForChainWallet[(collectibleAndAmount.communityToken.chainId, addressFrom)] = wholeEthCostForChainWallet.getOrDefault((collectibleAndAmount.communityToken.chainId, addressFrom), 0.0) + ethValue - ethValuesForContracts[(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address)] = ethValue - - var totalEthVal = 0.0 - var totalFiatVal = 0.0 - # for every contract create cost Args - for collectibleAndAmount in self.tempTokensAndAmounts: - let contractTuple = (chainId: collectibleAndAmount.communityToken.chainId, - address: collectibleAndAmount.communityToken.address) - let ethValue = ethValuesForContracts[contractTuple] - var balance = self.getWalletBalanceForChain(addressFrom, contractTuple.chainId) - if balance < wholeEthCostForChainWallet[(contractTuple.chainId, addressFrom)]: - # if wallet balance for this chain is less than the whole cost - # then we can't afford it; setting balance to 0.0 will set balance error code in Args - balance = 0.0 - dataToEmit.errorCode = ComputeFeeErrorCode.Balance # set total error code to balance error - var args = self.createComputeFeeArgsFromEthAndBalance(ethValue, balance) - totalEthVal = totalEthVal + ethValue - totalFiatVal = totalFiatVal + args.fiatCurrency.getAmount() - args.contractUniqueKey = common_utils.contractUniqueKey(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address) - allComputeFeeArgs.add(args) - - dataToEmit.fees = allComputeFeeArgs - let (ethTotal, fiatTotal) = self.createCurrencyAmounts(totalEthVal, totalFiatVal) - dataToEmit.totalEthFee = ethTotal - dataToEmit.totalFiatFee = fiatTotal - dataToEmit.requestId = requestId - self.events.emit(SIGNAL_COMPUTE_AIRDROP_FEE, dataToEmit) - - except Exception as e: - error "Error computing airdrop fees", msg = e.msg - dataToEmit.errorCode = ComputeFeeErrorCode.Other - dataToEmit.requestId = responseJson{"requestId"}.getStr - self.events.emit(SIGNAL_COMPUTE_AIRDROP_FEE, dataToEmit) + # proc parseFeeResponseAndEmitSignal(self:Service, response: string, signalName: string) = + # let responseJson = response.parseJson() + # try: + # let errorMessage = responseJson{"error"}.getStr + # if errorMessage != "": + # let data = self.createComputeFeeArgsWithError(errorMessage) + # data.requestId = responseJson{"requestId"}.getStr + # self.events.emit(signalName, data) + # return + # let gasTable = responseJson{"gasTable"}.toGasTable + # let feeTable = responseJson{"feeTable"}.toFeeTable + # let chainId = responseJson{"chainId"}.getInt + # let addressFrom = responseJson{"addressFrom"}.getStr + # self.tempGasTable = gasTable + # self.tempFeeTable = feeTable + # let gasUnits = toSeq(gasTable.values())[0] + # let suggestedFees = toSeq(feeTable.values())[0] + # let data = self.createComputeFeeArgs(gasUnits, suggestedFees, chainId, addressFrom) + # data.requestId = responseJson{"requestId"}.getStr + # self.events.emit(signalName, data) + # except Exception: + # error "Error creating fee args", message = getCurrentExceptionMsg() + # let data = self.createComputeFeeArgsWithError(getCurrentExceptionMsg()) + # data.requestId = responseJson{"requestId"}.getStr + # self.events.emit(signalName, data) + + # proc onDeployOwnerContractsFees*(self:Service, response: string) {.slot.} = + # self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_DEPLOY_FEE) + + # proc onSelfDestructFees*(self:Service, response: string) {.slot.} = + # self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_SELF_DESTRUCT_FEE) + + # proc onBurnFees*(self:Service, response: string) {.slot.} = + # self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_BURN_FEE) + + # proc onDeployFees*(self:Service, response: string) {.slot.} = + # self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_DEPLOY_FEE) + + # proc onSetSignerFees*(self: Service, response: string) {.slot.} = + # self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_SET_SIGNER_FEE) + + # proc onAirdropFees*(self:Service, response: string) {.slot.} = + # var wholeEthCostForChainWallet: Table[ChainWalletTuple, float] + # var ethValuesForContracts: Table[ContractTuple, float] + # var allComputeFeeArgs: seq[ComputeFeeArgs] + # var dataToEmit = AirdropFeesArgs() + # dataToEmit.errorCode = ComputeFeeErrorCode.Success + # let responseJson = response.parseJson() + + # try: + # let errorMessage = responseJson{"error"}.getStr + # let requestId = responseJson{"requestId"}.getStr + # if errorMessage != "": + # for collectibleAndAmount in self.tempTokensAndAmounts: + # let args = self.createComputeFeeArgsWithError(errorMessage) + # args.contractUniqueKey = common_utils.contractUniqueKey(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address) + # dataToEmit.fees.add(args) + # let (ethTotal, fiatTotal) = self.create0CurrencyAmounts() + # dataToEmit.totalEthFee = ethTotal + # dataToEmit.totalFiatFee = fiatTotal + # dataToEmit.errorCode = self.getErrorCodeFromMessage(errorMessage) + # dataToEmit.requestId = requestId + # self.events.emit(SIGNAL_COMPUTE_AIRDROP_FEE, dataToEmit) + # return + + # let gasTable = responseJson{"gasTable"}.toGasTable + # let feeTable = responseJson{"feeTable"}.toFeeTable + # let addressFrom = responseJson{"addressFrom"}.getStr + # self.tempGasTable = gasTable + # self.tempFeeTable = feeTable + + # # compute eth cost for every contract + # # also sum all eth costs per (chain, wallet) - it will be needed to compare with (chain, wallet) balance + # for collectibleAndAmount in self.tempTokensAndAmounts: + # let gasUnits = self.tempGasTable[(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address)] + # let suggestedFees = self.tempFeeTable[collectibleAndAmount.communityToken.chainId] + # let ethValue = self.computeEthValue(gasUnits, suggestedFees) + # wholeEthCostForChainWallet[(collectibleAndAmount.communityToken.chainId, addressFrom)] = wholeEthCostForChainWallet.getOrDefault((collectibleAndAmount.communityToken.chainId, addressFrom), 0.0) + ethValue + # ethValuesForContracts[(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address)] = ethValue + + # var totalEthVal = 0.0 + # var totalFiatVal = 0.0 + # # for every contract create cost Args + # for collectibleAndAmount in self.tempTokensAndAmounts: + # let contractTuple = (chainId: collectibleAndAmount.communityToken.chainId, + # address: collectibleAndAmount.communityToken.address) + # let ethValue = ethValuesForContracts[contractTuple] + # var balance = self.getWalletBalanceForChain(addressFrom, contractTuple.chainId) + # if balance < wholeEthCostForChainWallet[(contractTuple.chainId, addressFrom)]: + # # if wallet balance for this chain is less than the whole cost + # # then we can't afford it; setting balance to 0.0 will set balance error code in Args + # balance = 0.0 + # dataToEmit.errorCode = ComputeFeeErrorCode.Balance # set total error code to balance error + # var args = self.createComputeFeeArgsFromEthAndBalance(ethValue, balance) + # totalEthVal = totalEthVal + ethValue + # totalFiatVal = totalFiatVal + args.fiatCurrency.getAmount() + # args.contractUniqueKey = common_utils.contractUniqueKey(collectibleAndAmount.communityToken.chainId, collectibleAndAmount.communityToken.address) + # allComputeFeeArgs.add(args) + + # dataToEmit.fees = allComputeFeeArgs + # let (ethTotal, fiatTotal) = self.createCurrencyAmounts(totalEthVal, totalFiatVal) + # dataToEmit.totalEthFee = ethTotal + # dataToEmit.totalFiatFee = fiatTotal + # dataToEmit.requestId = requestId + # self.events.emit(SIGNAL_COMPUTE_AIRDROP_FEE, dataToEmit) + + # except Exception as e: + # error "Error computing airdrop fees", msg = e.msg + # dataToEmit.errorCode = ComputeFeeErrorCode.Other + # dataToEmit.requestId = responseJson{"requestId"}.getStr + # self.events.emit(SIGNAL_COMPUTE_AIRDROP_FEE, dataToEmit) proc isTokenDeployed(self: Service, token: CommunityTokenDto): bool = return token.deployState == DeployState.Deployed @@ -1440,4 +1497,16 @@ QtObject: if (tokenTupleKey != holdersTokenTuple): # different token is opened now return - self.restartTokenHoldersTimer(token.chainId, token.address) \ No newline at end of file + self.restartTokenHoldersTimer(token.chainId, token.address) + + proc stopSuggestedRoutesAsyncCalculation*(self: Service) = + self.transactionService.stopSuggestedRoutesAsyncCalculation() + + proc buildTransactionsFromRoute*(self: Service, uuid: string): string = + return self.transactionService.buildTransactionsFromRoute(uuid, slippagePercentage = 0.0) + + proc sendRouterTransactionsWithSignatures*(self: Service, uuid: string, signatures: TransactionsSignatures): string = + return self.transactionService.sendRouterTransactionsWithSignatures(uuid, signatures) + + proc signMessage*(self: Service, address: string, hashedPassword: string, hashedMessage: string): tuple[res: string, err: string] = + return self.transactionService.signMessage(address, hashedPassword, hashedMessage) \ No newline at end of file diff --git a/src/app_service/service/network/service.nim b/src/app_service/service/network/service.nim index 31104a9ad3e..23d37f57acd 100644 --- a/src/app_service/service/network/service.nim +++ b/src/app_service/service/network/service.nim @@ -77,6 +77,11 @@ proc getCurrentNetworksChainIds*(self: Service): seq[int] = proc getEnabledChainIds*(self: Service): seq[int] = return self.getCurrentNetworks().filter(n => n.isEnabled).map(n => n.chainId) +proc getDisabledChainIdsForEnabledChainIds*(self: Service, enabledChainIds: seq[int]): seq[int] = + for network in self.getCurrentNetworks(): + if not enabledChainIds.contains(network.chainId): + result.add(network.chainId) + proc upsertNetwork*(self: Service, network: NetworkItem): bool = let response = backend.addEthereumChain(backend.Network( chainId: network.chainId, diff --git a/src/app_service/service/transaction/dto.nim b/src/app_service/service/transaction/dto.nim index bb9f8efb0c2..6282c69d299 100644 --- a/src/app_service/service/transaction/dto.nim +++ b/src/app_service/service/transaction/dto.nim @@ -4,11 +4,13 @@ import Tables, sequtils import web3/ethtypes -include ../../common/json_utils -import ../network/dto, ../token/dto -import ../../common/conversion as service_conversion +import backend/transactions +import app_service/common/conversion as service_conversion +import app_service/service/network/dto +import app_service/service/token/dto +import app/modules/shared_models/currency_amount -import ./backend/transactions +include app_service/common/json_utils type SendType* {.pure.} = enum @@ -21,6 +23,13 @@ type ERC721Transfer ERC1155Transfer Swap + CommunityBurn + CommunityDeployAssets + CommunityDeployCollectibles + CommunityDeployOwnerToken + CommunityMintTokens + CommunityRemoteBurn + CommunitySetSignerPubKey Approve type @@ -355,6 +364,19 @@ type amountToReceive*: UInt256 toNetworks*: seq[SendToNetwork] +type + CostPerPath* = object + contractUniqueKey*: string + costEthCurrency*: CurrencyAmount + costFiatCurrency*: CurrencyAmount + +proc `%`*(self: CostPerPath): JsonNode = + result = %* { + "ethFee": if self.costEthCurrency == nil: newCurrencyAmount().toJsonNode() else: self.costEthCurrency.toJsonNode(), + "fiatFee": if self.costFiatCurrency == nil: newCurrencyAmount().toJsonNode() else: self.costFiatCurrency.toJsonNode(), + "contractUniqueKey": self.contractUniqueKey, + } + proc getGasEthValue*(gweiValue: float, gasLimit: uint64): float = let weiValue = service_conversion.gwei2Wei(gweiValue) * u256(gasLimit) let ethValue = parseFloat(service_conversion.wei2Eth(weiValue)) diff --git a/src/app_service/service/transaction/dtoV2.nim b/src/app_service/service/transaction/dtoV2.nim index 1c9eac7acee..ed44d4705df 100644 --- a/src/app_service/service/transaction/dtoV2.nim +++ b/src/app_service/service/transaction/dtoV2.nim @@ -30,6 +30,7 @@ type amountInLocked*: bool amountOut*: UInt256 suggestedLevelsForMaxFeesPerGas*: SuggestedLevelsForMaxFeesPerGasDto + usedContractAddress*: string txBaseFee*: UInt256 txPriorityFee*: UInt256 txGasAmount*: uint64 @@ -43,6 +44,7 @@ type approvalPriorityFee*: UInt256 approvalGasAmount*: uint64 approvalL1Fee*: UInt256 + txTotalFee*: UInt256 estimatedTime*: int proc toSuggestedLevelsForMaxFeesPerGasDto*(jsonObj: JsonNode): SuggestedLevelsForMaxFeesPerGasDto = @@ -66,6 +68,7 @@ proc toTransactionPathDtoV2*(jsonObj: JsonNode): TransactionPathDtoV2 = discard jsonObj.getProp("AmountInLocked", result.amountInLocked) result.amountOut = stint.fromHex(UInt256, jsonObj{"AmountOut"}.getStr) result.suggestedLevelsForMaxFeesPerGas = jsonObj["SuggestedLevelsForMaxFeesPerGas"].toSuggestedLevelsForMaxFeesPerGasDto() + discard jsonObj.getProp("UsedContractAddress", result.usedContractAddress) result.txBaseFee = stint.fromHex(UInt256, jsonObj{"TxBaseFee"}.getStr) result.txPriorityFee = stint.fromHex(UInt256, jsonObj{"TxPriorityFee"}.getStr) discard jsonObj.getProp("TxGasAmount", result.txGasAmount) @@ -79,6 +82,7 @@ proc toTransactionPathDtoV2*(jsonObj: JsonNode): TransactionPathDtoV2 = result.approvalPriorityFee = stint.fromHex(UInt256, jsonObj{"ApprovalPriorityFee"}.getStr) discard jsonObj.getProp("ApprovalGasAmount", result.approvalGasAmount) result.approvalL1Fee = stint.fromHex(UInt256, jsonObj{"ApprovalL1Fee"}.getStr) + result.txTotalFee = stint.fromHex(UInt256, jsonObj{"TxTotalFee"}.getStr) result.estimatedTime = jsonObj{"EstimatedTime"}.getInt proc toTransactionPathsDtoV2*(jsonObj: JsonNode): seq[TransactionPathDtoV2] = diff --git a/src/app_service/service/transaction/router_transactions_dto.nim b/src/app_service/service/transaction/router_transactions_dto.nim index b324545619e..bfbd8ae2eac 100644 --- a/src/app_service/service/transaction/router_transactions_dto.nim +++ b/src/app_service/service/transaction/router_transactions_dto.nim @@ -1,6 +1,7 @@ import json, stint +import app_service/service/community_tokens/dto/deployment_parameters -include ../../common/json_utils +include app_service/common/json_utils const TxStatusSending* = "Sending" @@ -12,6 +13,20 @@ type ErrorResponse* = ref object details*: string code*: string + +type + CommunityParamsDto* = ref object + communityID*: string + tokenContractAddress*: string + signerPubKey*: string + amount*: UInt256 + tokenIds*: seq[UInt256] + walletAddresses*: seq[string] + tokenDeploymentSignature*: string + ownerTokenParameters*: DeploymentParameters + masterTokenParameters*: DeploymentParameters + deploymentParameters*: DeploymentParameters + type SendDetailsDto* = ref object uuid*: string @@ -29,6 +44,7 @@ type username*: string publicKey*: string packId*: string + communityParams*: CommunityParamsDto type SigningDetails* = ref object @@ -77,6 +93,28 @@ proc toErrorResponse*(jsonObj: JsonNode): ErrorResponse = if jsonObj.contains("code"): result.code = jsonObj["code"].getStr +proc toCommunityParamsDto*(jsonObj: JsonNode): CommunityParamsDto = + result = CommunityParamsDto() + discard jsonObj.getProp("communityID", result.communityID) + discard jsonObj.getProp("tokenContractAddress", result.tokenContractAddress) + discard jsonObj.getProp("signerPubKey", result.signerPubKey) + discard jsonObj.getProp("tokenDeploymentSignature", result.tokenDeploymentSignature) + var tmpObj: JsonNode + if jsonObj.getProp("amount", tmpObj): + result.amount = stint.fromHex(UInt256, tmpObj.getStr) + if jsonObj.getProp("tokenIds", tmpObj) and tmpObj.kind == JArray: + for id in tmpObj: + result.tokenIds.add(stint.fromHex(UInt256, id.getStr)) + if jsonObj.getProp("walletAddresses", tmpObj) and tmpObj.kind == JArray: + for addr in tmpObj: + result.walletAddresses.add(addr.getStr) + if jsonObj.getProp("ownerTokenParameters", tmpObj): + result.ownerTokenParameters = toDeploymentParameters(tmpObj) + if jsonObj.getProp("masterTokenParameters", tmpObj): + result.masterTokenParameters = toDeploymentParameters(tmpObj) + if jsonObj.getProp("deploymentParameters", tmpObj): + result.deploymentParameters = toDeploymentParameters(tmpObj) + proc toSendDetailsDto*(jsonObj: JsonNode): SendDetailsDto = result = SendDetailsDto() discard jsonObj.getProp("uuid", result.uuid) @@ -100,6 +138,8 @@ proc toSendDetailsDto*(jsonObj: JsonNode): SendDetailsDto = if jsonObj.getProp("packId", tmpObj): let packId = stint.fromHex(UInt256, tmpObj.getStr) result.packId = $packId + if jsonObj.getProp("communityParams", tmpObj): + result.communityParams = toCommunityParamsDto(tmpObj) proc toSigningDetails*(jsonObj: JsonNode): SigningDetails = result = SigningDetails() diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index a05ed2a2f20..43550959827 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -6,25 +6,27 @@ import backend/backend import backend/eth import backend/wallet -import app_service/common/utils as common_utils -import app_service/common/types as common_types - import app/core/[main] import app/core/signals/types import app/core/tasks/[qt, threadpool] import app/global/global_singleton import app/global/app_signals +import app_service/common/wallet_constants as common_wallet_constants +import app_service/common/utils as common_utils +import app_service/common/types as common_types +import app_service/service/currency/service as currency_service import app_service/service/wallet_account/service as wallet_account_service import app_service/service/network/service as network_service import app_service/service/token/service as token_service import app_service/service/settings/service as settings_service +import app_service/service/eth/utils as eth_utils import ./dto as transaction_dto import ./dtoV2 import ./dto_conversion import ./router_transactions_dto -import app_service/service/eth/utils as eth_utils +import app/modules/shared_models/currency_amount export transaction_dto, router_transactions_dto export transactions.TransactionsSignatures @@ -46,7 +48,7 @@ const SIGNAL_TRANSACTION_DECODED* = "transactionDecoded" const SIGNAL_OWNER_TOKEN_SENT* = "ownerTokenSent" const SIGNAL_TRANSACTION_STATUS_CHANGED* = "transactionStatusChanged" -const InternalErrorCode = -1 +const InternalErrorCode* = -1 type TokenTransferMetadata* = object tokenName*: string @@ -114,9 +116,14 @@ type type SuggestedRoutesArgs* = ref object of Args uuid*: string + sendType*: SendType suggestedRoutes*: SuggestedRoutesDto errCode*: string errDescription*: string + # Below fields used for community related tx + costPerPath*: seq[CostPerPath] + totalCostEthCurrency*: CurrencyAmount + totalCostFiatCurrency*: CurrencyAmount type TransactionDecodedArgs* = ref object of Args @@ -137,10 +144,11 @@ QtObject: type Service* = ref object of QObject events: EventEmitter threadpool: ThreadPool + currencyService: currency_service.Service networkService: network_service.Service settingsService: settings_service.Service tokenService: token_service.Service - uuidOfTheLastRequestForSuggestedRoutes: string + lastRequestForSuggestedRoutes: tuple[uuid: string, sendType: SendType] ## Forward declarations proc suggestedRoutesReady(self: Service, uuid: string, route: seq[TransactionPathDtoV2], routeRaw: string, errCode: string, errDescription: string) @@ -152,6 +160,7 @@ QtObject: proc newService*( events: EventEmitter, threadpool: ThreadPool, + currencyService: currency_service.Service, networkService: network_service.Service, settingsService: settings_service.Service, tokenService: token_service.Service, @@ -160,6 +169,7 @@ QtObject: result.QObject.setup result.events = events result.threadpool = threadpool + result.currencyService = currencyService result.networkService = networkService result.settingsService = settingsService result.tokenService = tokenService @@ -345,8 +355,35 @@ QtObject: except Exception as e: error "Error getting suggested fees", msg = e.msg + proc updateCommunityRoute(self: Service, data: var SuggestedRoutesArgs, route: seq[TransactionPathDtoV2]) = + let ethFormat = self.currencyService.getCurrencyFormat(common_wallet_constants.ETH_SYMBOL) + let currencyFormat = self.currencyService.getCurrencyFormat(self.settingsService.getCurrency()) + let fiatPriceForSymbol = self.tokenService.getPriceBySymbol(ethFormat.symbol) + var totalFee: UInt256 + for p in route: + let feeInEth = wei2Eth(p.txTotalFee) + let ethFeeAsFloat = parseFloat(feeInEth) + let fiatFeeAsFloat = ethFeeAsFloat * fiatPriceForSymbol + data.costPerPath.add(CostPerPath( + contractUniqueKey: common_utils.contractUniqueKey(p.fromChain.chainId, p.usedContractAddress), + costEthCurrency: newCurrencyAmount(ethFeeAsFloat, ethFormat.symbol, int(ethFormat.displayDecimals), ethFormat.stripTrailingZeroes), + costFiatCurrency: newCurrencyAmount(fiatFeeAsFloat, currencyFormat.symbol, int(currencyFormat.displayDecimals), currencyFormat.stripTrailingZeroes) + )) + totalFee += p.txTotalFee + let totalFeeInEth = wei2Eth(totalFee) + let totalEthFeeAsFloat = parseFloat(totalFeeInEth) + data.totalCostEthCurrency = newCurrencyAmount(totalEthFeeAsFloat, ethFormat.symbol, int(ethFormat.displayDecimals), ethFormat.stripTrailingZeroes) + let totalFiatFeeAsFloat = totalEthFeeAsFloat * fiatPriceForSymbol + data.totalCostFiatCurrency = newCurrencyAmount(totalFiatFeeAsFloat, currencyFormat.symbol, int(currencyFormat.displayDecimals), currencyFormat.stripTrailingZeroes) + + proc emitSuggestedRoutesReadySignal*(self: Service, data: SuggestedRoutesArgs) = + if self.lastRequestForSuggestedRoutes.uuid != data.uuid: + error "cannot emit suggested routes ready signal, uuid mismatch" + return + self.events.emit(SIGNAL_SUGGESTED_ROUTES_READY, data) + proc suggestedRoutesReady(self: Service, uuid: string, route: seq[TransactionPathDtoV2], routeRaw: string, errCode: string, errDescription: string) = - if self.uuidOfTheLastRequestForSuggestedRoutes != uuid: + if self.lastRequestForSuggestedRoutes.uuid != uuid: return # TODO: refactor sending modal part of the app, but for now since we're integrating the router v2 just map params to the old dto @@ -359,12 +396,24 @@ QtObject: amountToReceive: getTotalAmountToReceive(oldRoute), toNetworks: getToNetworksList(oldRoute), ) - self.events.emit(SIGNAL_SUGGESTED_ROUTES_READY, SuggestedRoutesArgs( + var data = SuggestedRoutesArgs( uuid: uuid, + sendType: self.lastRequestForSuggestedRoutes.sendType, suggestedRoutes: suggestedDto, errCode: errCode, errDescription: errDescription - )) + ) + + if self.lastRequestForSuggestedRoutes.sendType == SendType.CommunityBurn or + self.lastRequestForSuggestedRoutes.sendType == SendType.CommunityDeployAssets or + self.lastRequestForSuggestedRoutes.sendType == SendType.CommunityDeployCollectibles or + self.lastRequestForSuggestedRoutes.sendType == SendType.CommunityDeployOwnerToken or + self.lastRequestForSuggestedRoutes.sendType == SendType.CommunityMintTokens or + self.lastRequestForSuggestedRoutes.sendType == SendType.CommunityRemoteBurn or + self.lastRequestForSuggestedRoutes.sendType == SendType.CommunitySetSignerPubKey: + self.updateCommunityRoute(data, route) + + self.emitSuggestedRoutesReadySignal(data) proc suggestedRoutes*(self: Service, uuid: string, @@ -381,7 +430,7 @@ QtObject: lockedInAmounts: Table[string, string] = initTable[string, string](), extraParamsTable: Table[string, string] = initTable[string, string]()) = - self.uuidOfTheLastRequestForSuggestedRoutes = uuid + self.lastRequestForSuggestedRoutes = (uuid, sendType) let bigAmountIn = common_utils.stringToUint256(amountIn) @@ -390,15 +439,49 @@ QtObject: amountOutHex = "0x" & eth_utils.stripLeadingZeros(bigAmountOut.toHex) try: - let res = eth.suggestedRoutesAsync(uuid, ord(sendType), accountFrom, accountTo, amountInHex, amountOutHex, token, + let err = wallet.suggestedRoutesAsync(uuid, ord(sendType), accountFrom, accountTo, amountInHex, amountOutHex, token, tokenIsOwnerToken, toToken, disabledFromChainIDs, disabledToChainIDs, lockedInAmounts, extraParamsTable) + if err.len > 0: + raise newException(CatchableError, "err fetching the best route: " & err) except CatchableError as e: error "suggestedRoutes", exception=e.msg self.suggestedRoutesReady(uuid, @[], "", $InternalErrorCode, e.msg) + proc suggestedCommunityRoutes*(self: Service, uuid: string, sendType: SendType, chainId: int, accountFrom: string, + communityId: string, signerPubKey: string = "", walletAddresses: seq[string] = @[], + transferDetails: seq[JsonNode] = @[], signature: string = "", ownerTokenParameters: JsonNode = JsonNode(), + masterTokenParameters: JsonNode = JsonNode(), deploymentParameters: JsonNode = JsonNode()) = + self.lastRequestForSuggestedRoutes = (uuid, sendType) + try: + let + disabledFromChainIDs = self.networkService.getDisabledChainIdsForEnabledChainIds(@[chainId]) + disabledToChainIDs = disabledFromChainIDs + + let err = wallet.suggestedRoutesAsyncForCommunities( + uuid, + ord(sendType), + accountFrom, + disabledFromChainIDs, + disabledToChainIDs, + communityId, + signerPubKey = singletonInstance.userProfile.getPubKey(), + tokenIds = @[], + walletAddresses = walletAddresses, + tokenDeploymentSignature = signature, + ownerTokenParameters, + masterTokenParameters, + deploymentParameters, + transferDetails + ) + if err.len > 0: + raise newException(CatchableError, "err fetching the best route for deploying owner: " & err) + except Exception as e: + error "Error loading fees", msg = e.msg + self.suggestedRoutesReady(uuid, @[], "", $InternalErrorCode, e.msg) + proc stopSuggestedRoutesAsyncCalculation*(self: Service) = try: - discard eth.stopSuggestedRoutesAsyncCalculation() + discard wallet.stopSuggestedRoutesAsyncCalculation() except CatchableError as e: error "stopSuggestedRoutesAsyncCalculation", exception=e.msg diff --git a/src/app_service/service/wallet_account/service.nim b/src/app_service/service/wallet_account/service.nim index 24c4767dd2d..71f6ba8c712 100644 --- a/src/app_service/service/wallet_account/service.nim +++ b/src/app_service/service/wallet_account/service.nim @@ -59,7 +59,6 @@ QtObject: proc parseCurrencyValueByTokensKey*(self: Service, tokensKey: string, amountInt: UInt256): float64 proc fetchENSNamesForAddressesAsync(self: Service, addresses: seq[string], chainId: int) # All slots defined in included files have to be forward declared - proc onAllTokensBuilt*(self: Service, response: string) {.slot.} proc onDerivedAddressesFetched*(self: Service, jsonString: string) {.slot.} proc onDerivedAddressesForMnemonicFetched*(self: Service, jsonString: string) {.slot.} proc onAddressDetailsFetched*(self: Service, jsonString: string) {.slot.} diff --git a/src/backend/community_tokens.nim b/src/backend/community_tokens.nim index 0134d08a04c..e6d554677da 100644 --- a/src/backend/community_tokens.nim +++ b/src/backend/community_tokens.nim @@ -1,6 +1,4 @@ import json, stint -import std/sequtils -import std/sugar import ./eth import ./core, ./response_type import ../app_service/service/community_tokens/dto/community_token @@ -8,16 +6,21 @@ import interpret/cropped_image from ./gen import rpc +include common + # Mirrors the transfer event from status-go, services/wallet/transfer/commands.go const eventCommunityTokenReceived*: string = "wallet-community-token-received" -proc deployCollectibles*(chainId: int, deploymentParams: JsonNode, txData: JsonNode, hashedPassword: string): RpcResponse[JsonNode] = - let payload = %* [chainId, deploymentParams, txData, hashedPassword] - return core.callPrivateRPC("communitytokens_deployCollectibles", payload) -proc deployAssets*(chainId: int, deploymentParams: JsonNode, txData: JsonNode, hashedPassword: string): RpcResponse[JsonNode] = - let payload = %* [chainId, deploymentParams, txData, hashedPassword] - return core.callPrivateRPC("communitytokens_deployAssets", payload) +proc storeDeployedCollectibles*(addressFrom: string, addressTo: string, chainId: int, txHash: string, deploymentParams: JsonNode): + RpcResponse[JsonNode] = + let payload = %* [addressFrom, addressTo, chainId, txHash, deploymentParams] + return core.callPrivateRPC("communitytokensv2_storeDeployedCollectibles", payload) + +proc storeDeployedAssets*(addressFrom: string, addressTo: string, chainId: int, txHash: string, deploymentParams: JsonNode): + RpcResponse[JsonNode] = + let payload = %* [addressFrom, addressTo, chainId, txHash, deploymentParams] + return core.callPrivateRPC("communitytokensv2_storeDeployedAssets", payload) proc removeCommunityToken*(chainId: int, address: string): RpcResponse[JsonNode] = let payload = %* [chainId, address] @@ -52,73 +55,36 @@ proc updateCommunityTokenSupply*(chainId: int, contractAddress: string, supply: let payload = %* [chainId, contractAddress, supply.toString(10)] return core.callPrivateRPC("wakuext_updateCommunityTokenSupply", payload) -proc mintTokens*(chainId: int, contractAddress: string, txData: JsonNode, hashedPasword: string, walletAddresses: seq[string], amount: Uint256): RpcResponse[JsonNode] = - let payload = %* [chainId, contractAddress, txData, hashedPasword, walletAddresses, amount.toString(10)] - return core.callPrivateRPC("communitytokens_mintTokens", payload) -proc estimateMintTokens*(chainId: int, contractAddress: string, fromAddress: string, walletAddresses: seq[string], amount: Uint256): RpcResponse[JsonNode] = - let payload = %* [chainId, contractAddress, fromAddress, walletAddresses, amount.toString(10)] - return core.callPrivateRPC("communitytokens_estimateMintTokens", payload) -proc remoteBurn*(chainId: int, contractAddress: string, txData: JsonNode, hashedPassword: string, tokenIds: seq[UInt256], additionalData: string): RpcResponse[JsonNode] = - let payload = %* [chainId, contractAddress, txData, hashedPassword, tokenIds.map(x => x.toString(10)), additionalData] - return core.callPrivateRPC("communitytokens_remoteBurn", payload) -proc estimateRemoteBurn*(chainId: int, contractAddress: string, fromAddress: string, tokenIds: seq[UInt256]): RpcResponse[JsonNode] = - let payload = %* [chainId, contractAddress, fromAddress, tokenIds.map(x => x.toString(10))] - return core.callPrivateRPC("communitytokens_estimateRemoteBurn", payload) -proc burn*(chainId: int, contractAddress: string, txData: JsonNode, hashedPassword: string, amount: Uint256): RpcResponse[JsonNode] = - let payload = %* [chainId, contractAddress, txData, hashedPassword, amount.toString(10)] - return core.callPrivateRPC("communitytokens_burn", payload) -proc estimateBurn*(chainId: int, contractAddress: string, fromAddress: string, amount: Uint256): RpcResponse[JsonNode] = - let payload = %* [chainId, contractAddress, fromAddress, amount.toString(10)] - return core.callPrivateRPC("communitytokens_estimateBurn", payload) -proc estimateSetSignerPubKey*(chainId: int, contractAddress: string, fromAddress: string, newSignerPubkey: string): RpcResponse[JsonNode] = - let payload = %* [chainId, contractAddress, fromAddress, newSignerPubkey] - return core.callPrivateRPC("communitytokens_estimateSetSignerPubKey", payload) proc remainingSupply*(chainId: int, contractAddress: string): RpcResponse[JsonNode] = let payload = %* [chainId, contractAddress] - return core.callPrivateRPC("communitytokens_remainingSupply", payload) + return core.callPrivateRPC("communitytokensv2_remainingSupply", payload) -proc deployCollectiblesEstimate*(chainId: int, addressFrom: string): RpcResponse[JsonNode] = - let payload = %*[chainId, addressFrom] - return core.callPrivateRPC("communitytokens_deployCollectiblesEstimate", payload) -proc deployAssetsEstimate*(chainId: int, addressFrom: string): RpcResponse[JsonNode] = - let payload = %*[chainId, addressFrom] - return core.callPrivateRPC("communitytokens_deployAssetsEstimate", payload) proc remoteDestructedAmount*(chainId: int, contractAddress: string): RpcResponse[JsonNode] = let payload = %*[chainId, contractAddress] - return core.callPrivateRPC("communitytokens_remoteDestructedAmount", payload) - -proc deployOwnerTokenEstimate*(chainId: int, addressFrom: string, ownerParams: JsonNode, masterParams: JsonNode, communityId: string, signerPubKey: string): RpcResponse[JsonNode] = - let payload = %*[chainId, addressFrom, ownerParams, masterParams, communityId, signerPubKey] - return core.callPrivateRPC("communitytokens_deployOwnerTokenEstimate", payload) - -proc deployOwnerToken*(chainId: int, ownerParams: JsonNode, masterParams: JsonNode, signerPubKey: string, txData: JsonNode, hashedPassword: string): RpcResponse[JsonNode] = - let payload = %*[chainId, ownerParams, masterParams, signerPubKey, txData, hashedPassword] - return core.callPrivateRPC("communitytokens_deployOwnerToken", payload) - -proc getMasterTokenContractAddressFromHash*(chainId: int, transactionHash: string): RpcResponse[JsonNode] = - let payload = %*[chainId, transactionHash] - return core.callPrivateRPC("communitytokens_getMasterTokenContractAddressFromHash", payload) + return core.callPrivateRPC("communitytokensv2_remoteDestructedAmount", payload) -proc getOwnerTokenContractAddressFromHash*(chainId: int, transactionHash: string): RpcResponse[JsonNode] = - let payload = %*[chainId, transactionHash] - return core.callPrivateRPC("communitytokens_getOwnerTokenContractAddressFromHash", payload) +proc storeDeployedOwnerToken*(addressFrom: string, chainId: int, txHash: string, ownerParams: JsonNode, masterParams: JsonNode): + RpcResponse[JsonNode] = + let payload = %* [addressFrom, chainId, txHash, ownerParams, masterParams] + return core.callPrivateRPC("communitytokensv2_storeDeployedOwnerToken", payload) -proc createCommunityTokenDeploymentSignature*(chainId: int, addressFrom: string, signerAddress: string): RpcResponse[JsonNode] = - let payload = %*[chainId, addressFrom, signerAddress] - return core.callPrivateRPC("wakuext_createCommunityTokenDeploymentSignature", payload) +proc createCommunityTokenDeploymentSignature*(resultOut: var JsonNode, chainId: int, addressFrom: string, signerAddress: string): string = + try: + let payload = %*[chainId, addressFrom, signerAddress] + let response = core.callPrivateRPC("wakuext_createCommunityTokenDeploymentSignature", payload) + return prepareResponse(resultOut, response) + except Exception as e: + return e.msg -proc setSignerPubKey*(chainId: int, contractAddress: string, txData: JsonNode, newSignerPubKey: string, hashedPassword: string): RpcResponse[JsonNode] = - let payload = %* [chainId, contractAddress, txData, hashedPassword, newSignerPubKey] - return core.callPrivateRPC("communitytokens_setSignerPubKey", payload) proc registerOwnerTokenReceivedNotification*(communityId: string): RpcResponse[JsonNode] = let payload = %*[communityId] @@ -142,11 +108,11 @@ proc registerLostOwnershipNotification*(communityId: string): RpcResponse[JsonNo proc getOwnerTokenOwnerAddress*(chainId: int, contractAddress: string): RpcResponse[JsonNode] = let payload = %*[chainId, contractAddress] - return core.callPrivateRPC("communitytokens_ownerTokenOwnerAddress", payload) + return core.callPrivateRPC("communitytokensv2_ownerTokenOwnerAddress", payload) proc reTrackOwnerTokenDeploymentTransaction*(chainId: int, contractAddress: string): RpcResponse[JsonNode] = let payload = %*[chainId, contractAddress] - return core.callPrivateRPC("communitytokens_reTrackOwnerTokenDeploymentTransaction", payload) + return core.callPrivateRPC("communitytokensv2_reTrackOwnerTokenDeploymentTransaction", payload) rpc(registerReceivedCommunityTokenNotification, "wakuext"): communityId: string diff --git a/src/backend/eth.nim b/src/backend/eth.nim index 4f39084665a..c4a46cc15d8 100644 --- a/src/backend/eth.nim +++ b/src/backend/eth.nim @@ -1,21 +1,9 @@ -import json, tables +import json import ./core, ./response_type from ./gen import rpc export response_type -const - GasFeeLow* = 0 - GasFeeMedium* = 1 - GasFeeHigh* = 2 - -const - ExtraKeyUsername* = "username" - ExtraKeyPublicKey* = "publicKey" - ExtraKeyPackId* = "packID" - - ExtraKeys = @[ExtraKeyUsername, ExtraKeyPublicKey, ExtraKeyPackId] - proc getAccounts*(): RpcResponse[JsonNode] = return core.callPrivateRPC("eth_accounts") @@ -38,57 +26,5 @@ proc suggestedFees*(chainId: int): RpcResponse[JsonNode] = let payload = %* [chainId] return core.callPrivateRPC("wallet_getSuggestedFees", payload) -proc prepareDataForSuggestedRoutes(uuid: string, sendType: int, accountFrom: string, accountTo: string, amountIn: string, amountOut: string, - token: string, tokenIsOwnerToken: bool, toToken: string, disabledFromChainIDs, disabledToChainIDs: seq[int], lockedInAmounts: Table[string, string], - extraParamsTable: Table[string, string]): JsonNode = - - let data = %* { - "uuid": uuid, - "sendType": sendType, - "addrFrom": accountFrom, - "addrTo": accountTo, - "amountIn": amountIn, - "amountOut": amountOut, - "tokenID": token, - "tokenIDIsOwnerToken": tokenIsOwnerToken, - "toTokenID": toToken, - "disabledFromChainIDs": disabledFromChainIDs, - "disabledToChainIDs": disabledToChainIDs, - "gasFeeMode": GasFeeMedium, - "fromLockedAmount": lockedInAmounts - } - - # `extraParamsTable` is used for send types like EnsRegister, EnsRelease, EnsSetPubKey, StickersBuy - # keys that can be used in `extraParamsTable` are: - # "username", "publicKey", "packID" - for key, value in extraParamsTable: - if key in ExtraKeys: - data[key] = %* value - else: - return nil - - return %* [data] - -proc suggestedRoutes*(sendType: int, accountFrom: string, accountTo: string, amountIn: string, amountOut: string, token: string, - tokenIsOwnerToken: bool, toToken: string, disabledFromChainIDs, disabledToChainIDs: seq[int], lockedInAmounts: Table[string, string], - extraParamsTable: Table[string, string]): RpcResponse[JsonNode] {.raises: [RpcException].} = - let payload = prepareDataForSuggestedRoutes(uuid = "", sendType, accountFrom, accountTo, amountIn, amountOut, token, tokenIsOwnerToken, toToken, - disabledFromChainIDs, disabledToChainIDs, lockedInAmounts, extraParamsTable) - if payload.isNil: - raise newException(RpcException, "Invalid key in extraParamsTable") - return core.callPrivateRPC("wallet_getSuggestedRoutes", payload) - -proc suggestedRoutesAsync*(uuid: string, sendType: int, accountFrom: string, accountTo: string, amountIn: string, amountOut: string, token: string, - tokenIsOwnerToken: bool, toToken: string, disabledFromChainIDs, disabledToChainIDs: seq[int], lockedInAmounts: Table[string, string], - extraParamsTable: Table[string, string]): RpcResponse[JsonNode] {.raises: [RpcException].} = - let payload = prepareDataForSuggestedRoutes(uuid, sendType, accountFrom, accountTo, amountIn, amountOut, token, tokenIsOwnerToken, toToken, - disabledFromChainIDs, disabledToChainIDs, lockedInAmounts, extraParamsTable) - if payload.isNil: - raise newException(RpcException, "Invalid key in extraParamsTable") - return core.callPrivateRPC("wallet_getSuggestedRoutesAsync", payload) - -proc stopSuggestedRoutesAsyncCalculation*() : RpcResponse[JsonNode] = - return core.callPrivateRPC("wallet_stopSuggestedRoutesAsyncCalculation") - rpc(getEstimatedLatestBlockNumber, "wallet"): chainId: int diff --git a/src/backend/wallet.nim b/src/backend/wallet.nim index ed59a7a04d0..126615ad13c 100644 --- a/src/backend/wallet.nim +++ b/src/backend/wallet.nim @@ -1,11 +1,26 @@ -import json, json_serialization, logging +import json, tables, json_serialization, logging import core, response_type from ./gen import rpc import status_go +from app_service/common/account_constants import ZERO_ADDRESS include common +export response_type + +const + GasFeeLow* = 0 + GasFeeMedium* = 1 + GasFeeHigh* = 2 + +const + ExtraKeyUsername* = "username" + ExtraKeyPublicKey* = "publicKey" + ExtraKeyPackId* = "packID" + + ExtraKeys = @[ExtraKeyUsername, ExtraKeyPublicKey, ExtraKeyPackId] + rpc(signMessage, "wallet"): message: string address: string @@ -102,4 +117,90 @@ proc hashTypedDataV4*(resultOut: var JsonNode, data: string): string = return prepareResponse(resultOut, response) except Exception as e: warn e.msg - return e.msg \ No newline at end of file + return e.msg + +proc prepareDataForSuggestedRoutes( + uuid: string, + sendType: int, + accountFrom: string, + accountTo: string, + amountIn: string, + amountOut: string, + token: string, + tokenIsOwnerToken: bool, + toToken: string, + disabledFromChainIDs, + disabledToChainIDs: seq[int], + lockedInAmounts: Table[string, string], + extraParamsTable: Table[string, string], + communityRouteInputParameters: JsonNode = JsonNode(), + ): JsonNode = + + let data = %* { + "uuid": uuid, + "sendType": sendType, + "addrFrom": accountFrom, + "addrTo": accountTo, + "amountIn": amountIn, + "amountOut": amountOut, + "tokenID": token, + "tokenIDIsOwnerToken": tokenIsOwnerToken, + "toTokenID": toToken, + "disabledFromChainIDs": disabledFromChainIDs, + "disabledToChainIDs": disabledToChainIDs, + "gasFeeMode": GasFeeMedium, + "fromLockedAmount": lockedInAmounts, + "communityRouteInputParams": communityRouteInputParameters, + } + + # `extraParamsTable` is used for send types like EnsRegister, EnsRelease, EnsSetPubKey, StickersBuy + # keys that can be used in `extraParamsTable` are: + # "username", "publicKey", "packID" + for key, value in extraParamsTable: + if key in ExtraKeys: + data[key] = %* value + else: + return nil + + return %* [data] + +proc suggestedRoutesAsync*(uuid: string, sendType: int, accountFrom: string, accountTo: string, amountIn: string, amountOut: string, token: string, + tokenIsOwnerToken: bool, toToken: string, disabledFromChainIDs, disabledToChainIDs: seq[int], lockedInAmounts: Table[string, string], + extraParamsTable: Table[string, string]): string {.raises: [RpcException].} = + let payload = prepareDataForSuggestedRoutes(uuid, sendType, accountFrom, accountTo, amountIn, amountOut, token, tokenIsOwnerToken, toToken, + disabledFromChainIDs, disabledToChainIDs, lockedInAmounts, extraParamsTable) + if payload.isNil: + raise newException(RpcException, "Invalid key in extraParamsTable") + let rpcResponse = core.callPrivateRPC("wallet_getSuggestedRoutesAsync", payload) + if isErrorResponse(rpcResponse): + return rpcResponse.error.message + +proc suggestedRoutesAsyncForCommunities*(uuid: string, sendType: int, accountFrom: string, disabledFromChainIDs, + disabledToChainIDs: seq[int], communityId: string, signerPubKey: string = "0x0", tokenIds: seq[string] = @[], + walletAddresses: seq[string] = @[], tokenDeploymentSignature: string = "", ownerTokenParameters: JsonNode = JsonNode(), + masterTokenParameters: JsonNode = JsonNode(), deploymentParameters: JsonNode = JsonNode(), + transferDetails: seq[JsonNode] = @[]): string {.raises: [RpcException].} = + + let data = %* { + "communityID": communityId, + "signerPubKey": signerPubKey, + "tokenIds": tokenIds, + "walletAddresses": walletAddresses, + "tokenDeploymentSignature": tokenDeploymentSignature, + "ownerTokenParameters": ownerTokenParameters, + "masterTokenParameters": masterTokenParameters, + "deploymentParameters": deploymentParameters, + "transferDetails": transferDetails, + } + + let payload = prepareDataForSuggestedRoutes(uuid, sendType, accountFrom, accountTo=ZERO_ADDRESS, amountIn="0x0", amountOut="0x0", token="ETH", + tokenIsOwnerToken=false, toToken="", disabledFromChainIDs, disabledToChainIDs, lockedInAmounts=initTable[string, string](), + extraParamsTable=initTable[string, string](), communityRouteInputParameters=data) + let rpcResponse = core.callPrivateRPC("wallet_getSuggestedRoutesAsync", payload) + if isErrorResponse(rpcResponse): + return rpcResponse.error.message + +proc stopSuggestedRoutesAsyncCalculation*(): string {.raises: [RpcException].} = + let rpcResponse = core.callPrivateRPC("wallet_stopSuggestedRoutesAsyncCalculation") + if isErrorResponse(rpcResponse): + return rpcResponse.error.message \ No newline at end of file diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/Subscription.qml b/ui/StatusQ/src/StatusQ/Core/Utils/Subscription.qml index 0144ef7c902..501e909fe9d 100644 --- a/ui/StatusQ/src/StatusQ/Core/Utils/Subscription.qml +++ b/ui/StatusQ/src/StatusQ/Core/Utils/Subscription.qml @@ -6,7 +6,6 @@ QtObject { readonly property string subscriptionId: Utils.uuid() property bool isReady: false - property int notificationInterval: 3000 // 1 notification every 3 seconds //The topic to subscribe to property string topic: "" property var response: {} diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/SubscriptionBroker.qml b/ui/StatusQ/src/StatusQ/Core/Utils/SubscriptionBroker.qml index fb7c2465601..0b9ee5c805b 100644 --- a/ui/StatusQ/src/StatusQ/Core/Utils/SubscriptionBroker.qml +++ b/ui/StatusQ/src/StatusQ/Core/Utils/SubscriptionBroker.qml @@ -6,13 +6,22 @@ import QtQuick 2.15 QtObject { id: root - signal request(string topic) + signal request(string subscriptionId, string topic) signal subscribed(string subscriptionId) signal unsubscribed(string subscriptionId) - function response(topic, responseObj) { - d.onResponse(topic, responseObj) + function response(subscriptionId, responseObj) { + let resolvedTopic = "" + Object.keys(d.topics).forEach(function (topic) { + d.topics[topic].subscriptions.forEach(function(subscrId) { + if(subscriptionId === subscrId) { + resolvedTopic = topic + return + } + }) + }) + d.onResponse(resolvedTopic, responseObj) } function subscribe(subscription) { d.subscribe(subscription) @@ -36,30 +45,11 @@ QtObject { readonly property var topics: ({}) property int topicsCount: 0 //helper property to track change events - property bool requestIntervalTriggered: false - readonly property int requestInterval: { - //dependency: - d.topicsCount - d.requestIntervalTriggered - const topicInfos = Object.values(d.topics) - if(!topicsCount || topicInfos.length === 0) - return 0 - const now = Date.now() - const interval = topicInfos.reduce((minInterval, topicInfo) => Math.max(0, Math.min(minInterval, topicInfo.nextRequestTimestamp - now)), Number.MAX_SAFE_INTEGER) - return interval > 0 ? interval : requestInterval - } - readonly property Timer requestTimer: Timer { - interval: d.requestInterval - repeat: true - running: interval > 0 && root.active - onTriggered: d.periodicRequest() - triggeredOnStart: true - } function subscribe(subscription) { if(!(subscription instanceof Subscription)) @@ -152,8 +142,6 @@ QtObject { function registerToTopic(topic, subscriptionId) { if(!d.topics.hasOwnProperty(topic)) { d.topics[topic] = { - requestInterval: d.managedSubscriptions[subscriptionId].subscription.notificationInterval, - nextRequestTimestamp: Date.now() + d.managedSubscriptions[subscriptionId].subscription.notificationInterval, subscriptions: [], response: null, requestPending: false @@ -169,7 +157,7 @@ QtObject { const subscriptionsCount = d.topics[topic].subscriptions.push(subscriptionId) if(subscriptionsCount === 1 && root.active) { - d.request(topic) + d.request(subscriptionId, topic) } d.managedSubscriptions[subscriptionId].subscription.response = d.topics[topic].response } @@ -187,32 +175,16 @@ QtObject { } } - function periodicRequest() { - if(!d.topics || !d.topicsCount) return - Object.entries(d.topics).forEach(function(entry) { - const topic = entry[0] - const topicInfo = entry[1] - if(!topicInfo || - !topicInfo.subscriptions || - !topicInfo.subscriptions.length || - topicInfo.requestPending || - topicInfo.nextRequestTimestamp > Date.now()) - return - d.request(topic) - }) - d.requestIntervalTriggered = !d.requestIntervalTriggered - } - function request(topic) { + function request(subscriptionId, topic) { if(!d.topics.hasOwnProperty(topic)) return d.topics[topic].requestPending = true - d.topics[topic].nextRequestTimestamp = Date.now() + d.topics[topic].requestInterval - root.request(topic) + root.request(subscriptionId, topic) } function onResponse(topic, responseObj) { diff --git a/ui/app/AppLayouts/Communities/helpers/AirdropFeesSubscriber.qml b/ui/app/AppLayouts/Communities/helpers/AirdropFeesSubscriber.qml index 3f2d0cc5558..bf8c789edb8 100644 --- a/ui/app/AppLayouts/Communities/helpers/AirdropFeesSubscriber.qml +++ b/ui/app/AppLayouts/Communities/helpers/AirdropFeesSubscriber.qml @@ -36,34 +36,33 @@ QtObject { property var airdropFeesResponse: null readonly property string feesError: { - if (!airdropFeesResponse) return "" - - if (airdropFeesResponse.errorCode === Constants.ComputeFeeErrorCode.Success) return "" - - if (airdropFeesResponse.errorCode === Constants.ComputeFeeErrorCode.Balance) - return qsTr("Your account does not have enough ETH to pay the gas fee for this airdrop. Try adding some ETH to your account.") - - if (airdropFeesResponse.errorCode === Constants.ComputeFeeErrorCode.Infura) - return qsTr("Infura error") - - if (airdropFeesResponse.errorCode === Constants.ComputeFeeErrorCode.Revert) - return qsTr("Estimation reverted. Make sure you selected the Wallet account that owns the TokenMaster or Owner Token.") + if (!root.airdropFeesResponse) + return "" - return qsTr("Unknown error") + return root.airdropFeesResponse.error } readonly property string totalFee: { - if (!airdropFeesResponse || !Object.values(airdropFeesResponse.totalEthFee).length || !Object.values(airdropFeesResponse.totalFiatFee).length) return "" + if (!root.airdropFeesResponse) { + return "" + } + + if (!!root.airdropFeesResponse.error) { + return "-" + } - if (airdropFeesResponse.errorCode !== Constants.ComputeFeeErrorCode.Success && airdropFeesResponse.errorCode !== Constants.ComputeFeeErrorCode.Balance) + if (!root.airdropFeesResponse || !Object.values(root.airdropFeesResponse.ethCurrency).length || !Object.values(root.airdropFeesResponse.fiatCurrency).length) { return "" + } - return `${LocaleUtils.currencyAmountToLocaleString(airdropFeesResponse.totalEthFee)} (${LocaleUtils.currencyAmountToLocaleString(airdropFeesResponse.totalFiatFee)})` + return LocaleUtils.currencyAmountToLocaleString(root.airdropFeesResponse.ethCurrency) + + " (" + LocaleUtils.currencyAmountToLocaleString(root.airdropFeesResponse.fiatCurrency) + ")" } readonly property var feesPerContract: { - if (!airdropFeesResponse || !Object.values(airdropFeesResponse.fees).length || totalFee == "") return [] + if (!root.airdropFeesResponse || !Object.values(root.airdropFeesResponse.fees).length || totalFee == "") + return [] - return airdropFeesResponse.fees.map(fee => { + return root.airdropFeesResponse.fees.map(fee => { return { contractUniqueKey: fee.contractUniqueKey, feeText: `${LocaleUtils.currencyAmountToLocaleString(fee.ethFee)} (${LocaleUtils.currencyAmountToLocaleString(fee.fiatFee)})` diff --git a/ui/app/AppLayouts/Communities/helpers/DeployFeesSubscriber.qml b/ui/app/AppLayouts/Communities/helpers/DeployFeesSubscriber.qml index 7bef0014359..1e0f12040c8 100644 --- a/ui/app/AppLayouts/Communities/helpers/DeployFeesSubscriber.qml +++ b/ui/app/AppLayouts/Communities/helpers/DeployFeesSubscriber.qml @@ -14,4 +14,8 @@ SingleFeeSubscriber { required property bool isOwnerDeployment required property string accountAddress required property bool enabled + + property var ownerToken + property var masterToken + property var token } diff --git a/ui/app/AppLayouts/Communities/helpers/SingleFeeSubscriber.qml b/ui/app/AppLayouts/Communities/helpers/SingleFeeSubscriber.qml index 9bde66a1f09..8a4b095954f 100644 --- a/ui/app/AppLayouts/Communities/helpers/SingleFeeSubscriber.qml +++ b/ui/app/AppLayouts/Communities/helpers/SingleFeeSubscriber.qml @@ -17,24 +17,26 @@ import utils 1.0 // Internal properties based on response readonly property string feeText: { - if (!feesResponse || !Object.values(feesResponse.ethCurrency).length || !Object.values(feesResponse.fiatCurrency).length) return "" - - if (feesResponse.errorCode !== Constants.ComputeFeeErrorCode.Success && feesResponse.errorCode !== Constants.ComputeFeeErrorCode.Balance) + if (!root.feesResponse) { return "" + } - return LocaleUtils.currencyAmountToLocaleString(feesResponse.ethCurrency) - + " (" + LocaleUtils.currencyAmountToLocaleString(feesResponse.fiatCurrency) + ")" - } - readonly property string feeErrorText: { - if (!feesResponse) return "" - if (feesResponse.errorCode === Constants.ComputeFeeErrorCode.Success) return "" - if (feesResponse.errorCode === Constants.ComputeFeeErrorCode.Balance) - return qsTr("Not enough funds to make transaction") + if (!!root.feesResponse.error) { + return "-" + } + + if (!root.feesResponse || !Object.values(root.feesResponse.ethCurrency).length || !Object.values(root.feesResponse.fiatCurrency).length) { + return "" + } - if (feesResponse.errorCode === Constants.ComputeFeeErrorCode.Infura) - return qsTr("Infura error") + return LocaleUtils.currencyAmountToLocaleString(root.feesResponse.ethCurrency) + + " (" + LocaleUtils.currencyAmountToLocaleString(root.feesResponse.fiatCurrency) + ")" + } + readonly property string feeErrorText: { + if (!root.feesResponse) + return "" - return qsTr("Unknown error") + return root.feesResponse.error } } diff --git a/ui/app/AppLayouts/Communities/helpers/TransactionFeesBroker.qml b/ui/app/AppLayouts/Communities/helpers/TransactionFeesBroker.qml index a71a871ffcc..de45923d3fe 100644 --- a/ui/app/AppLayouts/Communities/helpers/TransactionFeesBroker.qml +++ b/ui/app/AppLayouts/Communities/helpers/TransactionFeesBroker.qml @@ -3,6 +3,9 @@ import QtQuick 2.15 import shared.stores 1.0 import StatusQ.Core.Utils 0.1 +import AppLayouts.Wallet 1.0 + +import utils 1.0 QtObject { id: root @@ -51,7 +54,10 @@ QtObject { chainId: subscriber.chainId, accountAddress: subscriber.accountAddress, tokenType: subscriber.tokenType, - isOwnerDeployment: subscriber.isOwnerDeployment + isOwnerDeployment: subscriber.isOwnerDeployment, + ownerToken: subscriber.ownerToken, + masterToken: subscriber.masterToken, + token: subscriber.token }) isReady: !!subscriber.chainId && @@ -124,41 +130,49 @@ QtObject { readonly property SubscriptionBroker feesBroker: SubscriptionBroker { id: feesBroker - onRequest: internal.computeFee(topic) + onRequest: internal.computeFee(subscriptionId, topic) } property Connections communityTokensStoreConnections: Connections { - target: communityTokensStore - - function onDeployFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) { - d.feesBroker.response(responseId, { ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, errorCode: errorCode }) + target: root.communityTokensStore.communityTokensModuleInst + + function onSuggestedRoutesReady(uuid, ethCurrency, fiatCurrency, costPerPath, errCode, errDescription) { + let err = "" + if(!!errCode || !!errDescription) { + err = "%1 - %2".arg(errCode).arg(WalletUtils.getRouterErrorDetailsOnCode(errCode, errDescription)) + } + + let jsonFees = [{ + ethFee: {}, + fiatFee: {}, + contractUniqueKey: "" + }] + + if (!err && !!costPerPath) { + try { + jsonFees = JSON.parse(costPerPath) + } + catch (e) { + console.info("parsing fees issue: ", e.message) + } + } + + d.feesBroker.response(uuid, { ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, fees: jsonFees, error: err }) } - function onAirdropFeeUpdated(response) { - d.feesBroker.response(response.requestId, response) - } - function onSelfDestructFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) { - d.feesBroker.response(responseId, { ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, errorCode: errorCode }) - } - function onBurnFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) { - d.feesBroker.response(responseId, { ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, errorCode: errorCode }) - } - function onSetSignerFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) { - d.feesBroker.response(responseId, { ethCurrency: ethCurrency, fiatCurrency: fiatCurrency, errorCode: errorCode }) - } } - function computeFee(topic) { + function computeFee(subscriptionId, topic) { const args = JSON.parse(topic) switch (args.type) { case TransactionFeesBroker.FeeType.Airdrop: - computeAirdropFee(args, topic) + computeAirdropFee(subscriptionId, args) break case TransactionFeesBroker.FeeType.Deploy: - computeDeployFee(args, topic) + computeDeployFee(subscriptionId, args) break case TransactionFeesBroker.FeeType.SelfDestruct: computeSelfDestructFee(args, topic) @@ -174,17 +188,67 @@ QtObject { } } - function computeAirdropFee(args, topic) { + function computeAirdropFee(subscriptionId, args) { communityTokensStore.computeAirdropFee( + subscriptionId, args.communityId, args.contractKeysAndAmounts, args.addressesToAirdrop, - args.feeAccountAddress, - topic) + args.feeAccountAddress) } - function computeDeployFee(args, topic) { - communityTokensStore.computeDeployFee(args.communityId, args.chainId, args.accountAddress, args.tokenType, args.isOwnerDeployment, topic) + function computeDeployFee(subscriptionId, args) { + if (args.isOwnerDeployment) { + communityTokensStore.computeDeployTokenOwnerFee( + subscriptionId, + args.communityId, + args.ownerToken.chainId, + args.ownerToken.accountAddress, + args.ownerToken.name, + args.ownerToken.symbol, + args.ownerToken.description, + args.ownerToken.artworkSource, + args.ownerToken.artworkCropRect, + args.masterToken.name, + args.masterToken.symbol, + args.masterToken.description) + return + } + + if (args.tokenType === Constants.TokenType.ERC721) { + communityTokensStore.computeDeployCollectiblesFee( + subscriptionId, + args.communityId, + args.token.key, + args.token.chainId, + args.token.accountAddress, + args.token.name, + args.token.symbol, + args.token.description, + args.token.supply, + args.token.infiniteSupply, + args.token.transferable, + args.token.remotelyDestruct, + args.token.artworkSource, + args.token.artworkCropRect) + return + + } + + communityTokensStore.computeDeployAssetsFee( + subscriptionId, + args.communityId, + args.token.key, + args.token.chainId, + args.token.accountAddress, + args.token.name, + args.token.symbol, + args.token.description, + args.token.supply, + args.token.infiniteSupply, + args.token.decimals, + args.token.artworkSource, + args.token.artworkCropRect) } function computeSelfDestructFee(args, topic) { @@ -201,28 +265,27 @@ QtObject { } } - function registerAirdropFeesSubscriber(subscriberObj) { - const subscription = d.airdropFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) - d.feesBroker.subscribe(subscription) + function prepareAirdropFeesSubscribtion(subscriberObj) { + return d.airdropFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) } - function registerDeployFeesSubscriber(subscriberObj) { - const subscription = d.deployFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) - d.feesBroker.subscribe(subscription) + function prepareDeployFeesSubscribtion(subscriberObj) { + return d.deployFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) } - function registerSelfDestructFeesSubscriber(subscriberObj) { - const subscription = d.selfDestructFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) - d.feesBroker.subscribe(subscription) + function prepareSelfDestructFeesSubscribtion(subscriberObj) { + return d.selfDestructFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) } - function registerBurnFeesSubscriber(subscriberObj) { - const subscription = d.burnFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) - d.feesBroker.subscribe(subscription) + function prepareBurnFeesSubscribtion(subscriberObj) { + return d.burnFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) + } + + function prepareSetSignerFeesSubscribtion(subscriberObj) { + return d.setSignerFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) } - function registerSetSignerFeesSubscriber(subscriberObj) { - const subscription = d.setSignerFeeSubscriptionComponent.createObject(subscriberObj, { subscriber: subscriberObj }) + function subscribe(subscription) { d.feesBroker.subscribe(subscription) } } diff --git a/ui/app/AppLayouts/Communities/panels/AirdropsSettingsPanel.qml b/ui/app/AppLayouts/Communities/panels/AirdropsSettingsPanel.qml index bf7c55a85a9..7707c5d96db 100644 --- a/ui/app/AppLayouts/Communities/panels/AirdropsSettingsPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/AirdropsSettingsPanel.qml @@ -44,6 +44,7 @@ StackView { signal navigateToMintTokenSettings(bool isAssetType) signal registerAirdropFeeSubscriber(var feeSubscriber) signal enableNetwork(int chainId) + signal stopUpdatingFees() function navigateBack() { pop(StackView.Immediate) @@ -139,6 +140,14 @@ StackView { d.addAddresses.connect(view.addAddresses) } + onCalculateFees: { + root.registerAirdropFeeSubscriber(feesSubscriber) + } + + onStopUpdatingFees: { + root.stopUpdatingFees() + } + AirdropFeesSubscriber { id: feesSubscriber enabled: view.visible && view.showingFees @@ -146,7 +155,6 @@ StackView { contractKeysAndAmounts: view.selectedContractKeysAndAmounts addressesToAirdrop: view.selectedAddressesToAirdrop feeAccountAddress: view.selectedFeeAccount - Component.onCompleted: root.registerAirdropFeeSubscriber(feesSubscriber) } } } diff --git a/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml b/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml index a7a70d12a4d..10934ff98a5 100644 --- a/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml @@ -82,6 +82,7 @@ StackView { signal registerDeployFeesSubscriber(var feeSubscriber) signal registerSelfDestructFeesSubscriber(var feeSubscriber) signal registerBurnTokenFeesSubscriber(var feeSubscriber) + signal stopUpdatingFees() signal startTokenHoldersManagement(int chainId, string address) signal stopTokenHoldersManagement() @@ -269,6 +270,10 @@ StackView { isOwnerDeployment: editOwnerTokenView.ownerToken.isPrivilegedToken accountAddress: editOwnerTokenView.ownerToken.accountAddress enabled: editOwnerTokenView.visible || signMintPopup.visible + + ownerToken: editOwnerTokenView.ownerToken + masterToken: editOwnerTokenView.tMasterToken + Component.onCompleted: root.registerDeployFeesSubscriber(feeSubscriber) } @@ -407,6 +412,14 @@ StackView { StackView.Immediate) } + onCalculateFees: { + root.registerDeployFeesSubscriber(deployFeeSubscriber) + } + + onStopUpdatingFees: { + root.stopUpdatingFees() + } + DeployFeesSubscriber { id: deployFeeSubscriber communityId: root.communityId @@ -415,7 +428,8 @@ StackView { isOwnerDeployment: editView.token.isPrivilegedToken accountAddress: editView.token.accountAddress enabled: editView.visible - Component.onCompleted: root.registerDeployFeesSubscriber(deployFeeSubscriber) + + token: editView.token } } } @@ -464,7 +478,14 @@ StackView { isOwnerDeployment: preview.token.isPrivilegedToken accountAddress: preview.token.accountAddress enabled: preview.visible || signMintPopup.visible - Component.onCompleted: root.registerDeployFeesSubscriber(feeSubscriber) + + token: preview.token + + Component.onCompleted: { + Qt.callLater(function () { + root.registerDeployFeesSubscriber(feeSubscriber) + }) + } } SignTransactionsPopup { diff --git a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml index 751d09af20d..665111704d8 100644 --- a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml @@ -92,7 +92,10 @@ StatusSectionLayout { d.goTo(section, subSection) } - onBackButtonClicked: stackLayout.children[d.currentIndex].item.navigateBack() + onBackButtonClicked: { + root.rootStore.communityTokensStore.stopUpdatesForSuggestedRoute() + stackLayout.children[d.currentIndex].item.navigateBack() + } leftPanel: Item { anchors.fill: parent @@ -179,6 +182,7 @@ StatusSectionLayout { currentIndex: d.currentIndex onCurrentIndexChanged: { + root.rootStore.communityTokensStore.stopUpdatesForSuggestedRoute() children[currentIndex].active = true } @@ -368,8 +372,11 @@ StatusSectionLayout { readonly property bool sectionEnabled: true sourceComponent: MintTokensSettingsPanel { + id: mintTokensSettingsPanel enabledChainIds: root.enabledChainIds + property string subscriptionId + readonly property CommunityTokensStore communityTokensStore: rootStore.communityTokensStore @@ -397,49 +404,85 @@ StatusSectionLayout { accounts: root.walletAccountsModel referenceAssetsBySymbolModel: root.tokensStore.assetsBySymbolModel - onRegisterDeployFeesSubscriber: d.feesBroker.registerDeployFeesSubscriber(feeSubscriber) + onStopUpdatingFees: { + communityTokensStore.stopUpdatesForSuggestedRoute() + } + + onRegisterDeployFeesSubscriber: { + const subscription = d.feesBroker.prepareDeployFeesSubscribtion(feeSubscriber) + mintTokensSettingsPanel.subscriptionId = subscription.subscriptionId + d.feesBroker.subscribe(subscription) + } + + onRegisterSelfDestructFeesSubscriber: { + const subscription = d.feesBroker.prepareSelfDestructFeesSubscribtion(feeSubscriber) + mintTokensSettingsPanel.subscriptionId = subscription.subscriptionId + d.feesBroker.subscribe(subscription) + } + + onRegisterBurnTokenFeesSubscriber: { + - onRegisterSelfDestructFeesSubscriber: d.feesBroker.registerSelfDestructFeesSubscriber(feeSubscriber) - onRegisterBurnTokenFeesSubscriber: d.feesBroker.registerBurnFeesSubscriber(feeSubscriber) + const subscription = d.feesBroker.prepareBurnFeesSubscribtion(feeSubscriber) + mintTokensSettingsPanel.subscriptionId = subscription.subscriptionId + d.feesBroker.subscribe(subscription) + } - onStartTokenHoldersManagement: communityTokensStore.startTokenHoldersManagement(root.community.id, chainId, address) + onStartTokenHoldersManagement: { + communityTokensStore.startTokenHoldersManagement(root.community.id, chainId, address) + } - onStopTokenHoldersManagement: communityTokensStore.stopTokenHoldersManagement() + onStopTokenHoldersManagement: { + communityTokensStore.stopTokenHoldersManagement() + } onEnableNetwork: root.enableNetwork(chainId) - onMintCollectible: - communityTokensStore.deployCollectible( - root.community.id, collectibleItem) + onMintCollectible: { + communityTokensStore.authenticateAndTransfer() + } + + onMintAsset: { - onMintAsset: - communityTokensStore.deployAsset(root.community.id, assetItem) + communityTokensStore.authenticateAndTransfer() + } - onMintOwnerToken: - communityTokensStore.deployOwnerToken( - root.community.id, ownerToken, tMasterToken) + onMintOwnerToken: { + communityTokensStore.authenticateAndTransfer() + } - onRemotelyDestructCollectibles: + onRemotelyDestructCollectibles: { communityTokensStore.remoteSelfDestructCollectibles( root.community.id, walletsAndAmounts, tokenKey, accountAddress) + } + + onRemotelyDestructAndBan: { - onRemotelyDestructAndBan: communityTokensStore.remotelyDestructAndBan( root.community.id, contactId, tokenKey, accountAddress) + } + + onRemotelyDestructAndKick: { - onRemotelyDestructAndKick: communityTokensStore.remotelyDestructAndKick( root.community.id, contactId, tokenKey, accountAddress) + } + + onBurnToken: { - onBurnToken: communityTokensStore.burnToken(root.community.id, tokenKey, amount, accountAddress) + } + + onDeleteToken: { - onDeleteToken: communityTokensStore.deleteToken(root.community.id, tokenKey) + } + + onRefreshToken: { - onRefreshToken: communityTokensStore.refreshToken(tokenKey) + } onAirdropToken: { root.goTo(Constants.CommunitySettingsSections.Airdrops) @@ -451,8 +494,13 @@ StatusSectionLayout { airdropPanelLoader.item.addAddresses(addresses) } - onKickUserRequested: root.rootStore.removeUserFromCommunity(contactId) - onBanUserRequested: root.rootStore.banUserFromCommunity(contactId) + onKickUserRequested: { + root.rootStore.removeUserFromCommunity(contactId) + } + + onBanUserRequested: { + root.rootStore.banUserFromCommunity(contactId) + } } } @@ -466,6 +514,8 @@ StatusSectionLayout { readonly property string sectionIcon: "airdrop" readonly property bool sectionEnabled: true + property string subscriptionId + sourceComponent: AirdropsSettingsPanel { id: airdropsSettingsPanel @@ -564,16 +614,25 @@ StatusSectionLayout { onEnableNetwork: root.enableNetwork(chainId) accountsModel: root.walletAccountsModel - onAirdropClicked: communityTokensStore.airdrop( - root.community.id, airdropTokens, addresses, - feeAccountAddress) + onAirdropClicked: { + communityTokensStore.authenticateAndTransfer() + } onNavigateToMintTokenSettings: { root.goTo(Constants.CommunitySettingsSections.MintTokens) mintPanelLoader.item.openNewTokenForm(isAssetType) } - onRegisterAirdropFeeSubscriber: d.feesBroker.registerAirdropFeesSubscriber(feeSubscriber) + onStopUpdatingFees: { + communityTokensStore.stopUpdatesForSuggestedRoute() + } + + onRegisterAirdropFeeSubscriber: { + const subscription = d.feesBroker.prepareAirdropFeesSubscribtion(feeSubscriber) + airdropPanelLoader.subscriptionId = subscription.subscriptionId + d.feesBroker.subscribe(subscription) + } + } } } @@ -738,35 +797,6 @@ StatusSectionLayout { loading, type, url) } - function onAirdropStateChanged(communityId, tokenName, chainName, - status, url) { - if (root.community.id !== communityId) - return - - let title = "" - let loading = false - let type = Constants.ephemeralNotificationType.normal - - switch (status) { - case Constants.ContractTransactionStatus.InProgress: - title = qsTr("Airdrop on %1 in progress...").arg(chainName) - loading = true - break - case Constants.ContractTransactionStatus.Completed: - title = qsTr("Airdrop on %1 in complete").arg(chainName) - type = Constants.ephemeralNotificationType.success - break - case Constants.ContractTransactionStatus.Failed: - title = qsTr("Airdrop on %1 failed").arg(chainName) - break - default: - console.warn("Unknown airdrop state: "+status) - return - } - - Global.displayToastMessage(title, qsTr("View on etherscan"), "", - loading, type, url) - } function onBurnStateChanged(communityId, tokenName, status, url) { if (root.community.id !== communityId) @@ -797,67 +827,6 @@ StatusSectionLayout { loading, type, url) } - function onDeploymentStateChanged(communityId, status, url) { - if (root.community.id !== communityId) - return - - let title = "" - let loading = false - let type = Constants.ephemeralNotificationType.normal - - switch (status) { - case Constants.ContractTransactionStatus.InProgress: - title = qsTr("Token is being minted...") - loading = true - break - case Constants.ContractTransactionStatus.Completed: - title = qsTr("Token minting finished") - type = Constants.ephemeralNotificationType.success - break - case Constants.ContractTransactionStatus.Failed: - title = qsTr("Token minting failed") - break - default: - console.warn("Unknown deploy state: "+status) - return - } - - Global.displayToastMessage(title, url === "" ? qsTr("Something went wrong") : qsTr("View on etherscan"), "", - loading, type, url) - } - - function onOwnerTokenDeploymentStateChanged(communityId, status, url) { - if (root.community.id !== communityId) - return - - if (status === Constants.ContractTransactionStatus.Completed) - { - let title1 = qsTr("%1 Owner and TokenMaster tokens minting complete").arg(community.name) - let title2 = qsTr("%1 Owner token airdropped to you").arg(community.name) - let title3 = qsTr("%1 Owner and TokenMaster permissions created").arg(community.name) - let type = Constants.ephemeralNotificationType.normal - - Global.displayToastMessage(title1, url, "", true, type, url) - Global.displayToastMessage(title2, url, "", true, type, url) - Global.displayToastMessage(title3, url, "", true, type, url) - } else if (status === Constants.ContractTransactionStatus.Failed) { - let title = qsTr("%1 Owner and TokenMaster tokens minting failed").arg(community.name) - let type = Constants.ephemeralNotificationType.normal - Global.displayToastMessage(title, url, "", true, type, url) - } - } - - function onOwnerTokenDeploymentStarted(communityId, url) { - if (root.community.id !== communityId) - return - - let title1 = qsTr("%1 Owner and TokenMaster tokens are being minted...").arg(community.name) - let title2 = qsTr("Airdropping %1 Owner token to you...").arg(community.name) - let type = Constants.ephemeralNotificationType.normal - - Global.displayToastMessage(title1, url, "", true, type, url) - Global.displayToastMessage(title2, url, "", true, type, url) - } } Connections { diff --git a/ui/app/AppLayouts/Communities/views/EditAirdropView.qml b/ui/app/AppLayouts/Communities/views/EditAirdropView.qml index 678c7d48a46..1475f1841bb 100644 --- a/ui/app/AppLayouts/Communities/views/EditAirdropView.qml +++ b/ui/app/AppLayouts/Communities/views/EditAirdropView.qml @@ -60,12 +60,16 @@ StatusScrollView { // Bool property indicating whether the fees are shown readonly property bool showingFees: d.showFees + signal calculateFees() + signal stopUpdatingFees() + onFeesPerSelectedContractChanged: { feesModel.clear() let feeSource = feesPerSelectedContract - if(!feeSource || feeSource.length === 0) // if no fees are available, show the placeholder text based on selection + if(!feeSource || feeSource.length === 0) { // if no fees are available, show the placeholder text based on selection feeSource = ModelUtils.modelToArray(root.selectedHoldingsModel, ["contractUniqueKey"]) + } feeSource.forEach(entry => { feesModel.append({ @@ -254,6 +258,7 @@ StatusScrollView { AirdropTokensSelector { id: tokensSelector + enabled: !showFees.checked property int editedIndex: -1 @@ -406,6 +411,7 @@ StatusScrollView { AirdropRecipientsSelector { id: airdropRecipientsSelector + enabled: !showFees.checked addressesModel: addresses @@ -557,8 +563,25 @@ StatusScrollView { SequenceColumnLayout.Separator {} + StatusSwitch { + id: showFees + enabled: root.isFullyFilled + text: qsTr("Show fees (will be enabled once the form is filled)") + + onCheckedChanged: { + if(checked) { + root.calculateFees() + return + } + root.stopUpdatingFees() + } + } + + SequenceColumnLayout.Separator {} + FeesBox { id: feesBox + visible: showFees.checked Layout.fillWidth: true model: feesModel diff --git a/ui/app/AppLayouts/Communities/views/EditCommunityTokenView.qml b/ui/app/AppLayouts/Communities/views/EditCommunityTokenView.qml index a77398a7a75..4b62fdd50ea 100644 --- a/ui/app/AppLayouts/Communities/views/EditCommunityTokenView.qml +++ b/ui/app/AppLayouts/Communities/views/EditCommunityTokenView.qml @@ -53,17 +53,21 @@ StatusScrollView { signal chooseArtWork signal previewClicked + signal calculateFees() + signal stopUpdatingFees() QtObject { id: d - readonly property bool isFullyFilled: dropAreaItem.artworkSource.toString().length > 0 + readonly property bool formFilled: dropAreaItem.artworkSource.toString().length > 0 && nameInput.valid && descriptionInput.valid && symbolInput.valid && (unlimitedSupplyChecker.checked || (!unlimitedSupplyChecker.checked && parseInt(supplyInput.text) > 0)) && (!root.isAssetView || (root.isAssetView && assetDecimalsInput.valid)) - && deployFeeSubscriber.feeText !== "" && deployFeeSubscriber.feeErrorText === "" + readonly property bool isFullyFilled: d.formFilled + && deployFeeSubscriber.feeText !== "" + && deployFeeSubscriber.feeErrorText === "" readonly property int imageSelectorRectWidth: root.isAssetView ? 128 : 290 @@ -110,6 +114,7 @@ StatusScrollView { DropAndEditImagePanel { id: dropAreaItem + enabled: !showFees.checked Layout.fillWidth: true Layout.preferredHeight: d.imageSelectorRectWidth dataImage: root.token.artworkSource @@ -129,6 +134,7 @@ StatusScrollView { CustomStatusInput { id: nameInput + enabled: !showFees.checked label: qsTr("Name") text: root.token.name charLimit: 15 @@ -157,6 +163,7 @@ StatusScrollView { CustomStatusInput { id: descriptionInput + enabled: !showFees.checked label: qsTr("Description") text: root.token.description charLimit: 280 @@ -177,6 +184,7 @@ StatusScrollView { CustomStatusInput { id: symbolInput + enabled: !showFees.checked label: qsTr("Symbol") text: root.token.symbol charLimit: 6 @@ -258,6 +266,7 @@ StatusScrollView { NetworkWarningPanel { visible: !!root.networkThatIsNotActive + enabled: !showFees.checked Layout.fillWidth: true Layout.topMargin: Theme.padding @@ -268,6 +277,7 @@ StatusScrollView { CustomSwitchRowComponent { id: unlimitedSupplyChecker + enabled: !showFees.checked label: qsTr("Unlimited supply") description: qsTr("Enable to allow the minting of additional tokens in the future. Disable to specify a finite supply") checked: root.token.infiniteSupply @@ -310,6 +320,7 @@ StatusScrollView { id: transferableChecker visible: !root.isAssetView + enabled: !showFees.checked label: checked ? qsTr("Not transferable (Soulbound)") : qsTr("Transferable") description: qsTr("If enabled, the token is locked to the first address it is sent to and can never be transferred to another address. Useful for tokens that represent Admin permissions") checked: !root.token.transferable @@ -321,6 +332,7 @@ StatusScrollView { id: remotelyDestructChecker visible: !root.isAssetView + enabled: !showFees.checked label: qsTr("Remotely destructible") description: qsTr("Enable to allow you to destroy tokens remotely. Useful for revoking permissions from individuals") checked: !!root.token ? root.token.remotelyDestruct : true @@ -347,9 +359,26 @@ StatusScrollView { onTextChanged: root.token.decimals = parseInt(text) } + CustomSwitchRowComponent { + id: showFees + enabled: d.formFilled + label: qsTr("Show fees") + description: qsTr("Fees will be enabled once the form is filled") + + onCheckedChanged: { + if(checked) { + root.calculateFees() + return + } + root.stopUpdatingFees() + } + } + FeesBox { id: feesBox + visible: showFees.checked + Layout.fillWidth: true Layout.topMargin: Theme.padding diff --git a/ui/app/AppLayouts/Communities/views/MintedTokensView.qml b/ui/app/AppLayouts/Communities/views/MintedTokensView.qml index 2ad0537a451..6ea52d35154 100644 --- a/ui/app/AppLayouts/Communities/views/MintedTokensView.qml +++ b/ui/app/AppLayouts/Communities/views/MintedTokensView.qml @@ -256,9 +256,11 @@ StatusScrollView { privilegesLevel: model.privilegesLevel ornamentColor: model.color communityId: "" - onClicked: root.itemClicked(model.contractUniqueKey, + onClicked: { + root.itemClicked(model.contractUniqueKey, model.chainId, model.chainName, model.accountName, model.address) + } } } diff --git a/ui/app/AppLayouts/Wallet/services/dapps/internal/TransactionFeesBroker.qml b/ui/app/AppLayouts/Wallet/services/dapps/internal/TransactionFeesBroker.qml index 459d278d7db..0ba33255686 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/internal/TransactionFeesBroker.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/internal/TransactionFeesBroker.qml @@ -112,7 +112,6 @@ SQUtils.QObject { return subscriber.setEstimatedTime(response.estimatedTime) } - notificationInterval: root.interval } } @@ -132,7 +131,6 @@ SQUtils.QObject { return subscriber.setGas(response.gasEstimate) } - notificationInterval: root.interval } } diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index c21dbf6165e..7e9394c3b60 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -79,7 +79,9 @@ Item { property ChatStores.CreateChatPropertiesStore createChatPropertiesStore: ChatStores.CreateChatPropertiesStore {} property ActivityCenterStore activityCenterStore: ActivityCenterStore {} property SharedStores.NetworkConnectionStore networkConnectionStore: SharedStores.NetworkConnectionStore {} - property SharedStores.CommunityTokensStore communityTokensStore: SharedStores.CommunityTokensStore {} + property SharedStores.CommunityTokensStore communityTokensStore: SharedStores.CommunityTokensStore { + currencyStore: appMain.currencyStore + } property CommunitiesStore communitiesStore: CommunitiesStore {} readonly property WalletStores.TokensStore tokensStore: WalletStores.RootStore.tokensStore readonly property WalletStores.WalletAssetsStore walletAssetsStore: WalletStores.RootStore.walletAssetsStore @@ -285,6 +287,13 @@ Item { username: string, publicKey: string, packId: string, + communityId: string, + communityName: string, + communityAmount: string, + communityAssetName: string, + communityAssetDecimals: int, + communityOwnerTokenName: string, + communityMasterTokenName: string, status: string, error: string) { @@ -308,6 +317,8 @@ Item { let ensName = d.ensName(username) let stickersPackName = qsTr("unknown") + let sentCommunityAmount = "" + if (!!txHash) { toastLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(fromChainId)).arg(txHash) } @@ -350,6 +361,11 @@ Item { } } + if (!!communityAmount) { + let bigIntCommunityAmount = SQUtils.AmountsArithmetic.fromString(communityAmount) + sentCommunityAmount = SQUtils.AmountsArithmetic.toNumber(bigIntCommunityAmount, communityAssetDecimals) + } + switch(status) { case Constants.txStatus.sending: { toastTitle = qsTr("Sending %1 from %2 to %3") @@ -398,6 +414,22 @@ Item { } break } + case Constants.SendType.CommunityDeployAssets: { + toastTitle = qsTr("Minting %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityDeployCollectibles: { + toastTitle = qsTr("Minting %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityDeployOwnerToken: { + toastTitle = qsTr("Minting %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityMintTokens: { + toastTitle = qsTr("Minting %1x %2 on %3 using %4").arg(sentCommunityAmount).arg(communityAssetName).arg(communityName).arg(sender) + break + } case Constants.SendType.Approve: { console.warn("tx type approve not yet identified as a stand alone path") break @@ -462,6 +494,22 @@ Item { } break } + case Constants.SendType.CommunityDeployAssets: { + toastTitle = qsTr("Minted %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityDeployCollectibles: { + toastTitle = qsTr("Minted %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityDeployOwnerToken: { + toastTitle = qsTr("Minted %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityMintTokens: { + toastTitle = qsTr("Minted %1x %2 on %3 using %4").arg(sentCommunityAmount).arg(communityAssetName).arg(communityName).arg(sender) + break + } case Constants.SendType.Approve: { console.warn("tx type approve not yet identified as a stand alone path") break @@ -520,6 +568,22 @@ Item { } break } + case Constants.SendType.CommunityDeployAssets: { + toastTitle = qsTr("Mint failed: %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityDeployCollectibles: { + toastTitle = qsTr("Mint failed: %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityDeployOwnerToken: { + toastTitle = qsTr("Mint failed: %1 and %2 tokens for %3 using %4").arg(communityOwnerTokenName).arg(communityMasterTokenName).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunityMintTokens: { + toastTitle = qsTr("Mint failed: %1x %2 on %3 using %4").arg(sentCommunityAmount).arg(communityAssetName).arg(communityName).arg(sender) + break + } case Constants.SendType.Approve: { console.warn("tx type approve not yet identified as a stand alone path") break @@ -531,8 +595,16 @@ Item { break } default: - console.warn("not supported status") - return + if (!error) { + console.warn("not supported status") + return + } else { + const err1 = "cannot_resolve_community" // move to Constants + if (error === err1) { + Global.displayToastMessage(qsTr("Unknown error resolving community"), "", "", false, Constants.ephemeralNotificationType.normal, "") + return + } + } } Global.displayToastMessage(toastTitle, toastSubtitle, toastIcon, toastLoading, toastType, toastLink) diff --git a/ui/app/mainui/Popups.qml b/ui/app/mainui/Popups.qml index fe106fce16e..8f57b62042b 100644 --- a/ui/app/mainui/Popups.qml +++ b/ui/app/mainui/Popups.qml @@ -1123,7 +1123,9 @@ QtObject { contractAddress: finalisePopup.ownerTokenDetails.contractAddress accountAddress: finalisePopup.ownerTokenDetails.accountAddress enabled: finalisePopup.visible || signPopup.visible - Component.onCompleted: feesBroker.registerSetSignerFeesSubscriber(feeSubscriber) + Component.onCompleted: { + const subscription = d.feesBroker.prepareSetSignerFeesSubscribtion(feeSubscriber) + } } SignTransactionsPopup { diff --git a/ui/imports/shared/stores/CommunityTokensStore.qml b/ui/imports/shared/stores/CommunityTokensStore.qml index eac1a210e22..b03141ad705 100644 --- a/ui/imports/shared/stores/CommunityTokensStore.qml +++ b/ui/imports/shared/stores/CommunityTokensStore.qml @@ -6,6 +6,8 @@ import utils 1.0 QtObject { id: root + property CurrenciesStore currencyStore + property var communityTokensModuleInst: communityTokensModule ?? null property var mainModuleInst: mainModule ?? null @@ -21,18 +23,13 @@ QtObject { JSON.parse(communityTokensModuleInst.ownerTokenDetails) } - signal deployFeeUpdated(var ethCurrency, var fiatCurrency, int error, string responseId) signal selfDestructFeeUpdated(var ethCurrency, var fiatCurrency, int error, string responseId) - signal airdropFeeUpdated(var airdropFees) signal burnFeeUpdated(var ethCurrency, var fiatCurrency, int error, string responseId) signal setSignerFeeUpdated(var ethCurrency, var fiatCurrency, int error, string responseId) - signal deploymentStateChanged(string communityId, int status, string url) - signal ownerTokenDeploymentStateChanged(string communityId, int status, string url) signal remoteDestructStateChanged(string communityId, string tokenName, int status, string url) signal burnStateChanged(string communityId, string tokenName, int status, string url) signal airdropStateChanged(string communityId, string tokenName, string chainName, int status, string url) - signal ownerTokenDeploymentStarted(string communityId, string url) signal setSignerStateChanged(string communityId, string communityName, int status, string url) signal ownershipLost(string communityId, string communityName) signal communityOwnershipDeclined(string communityName) @@ -45,47 +42,63 @@ QtObject { int tokenType, string walletAccountName, string walletAddress) - // Minting tokens: - function deployCollectible(communityId, collectibleItem) { - if (collectibleItem.key !== "") - deleteToken(communityId, collectibleItem.key) + function authenticateAndTransfer() { + communityTokensModuleInst.authenticateAndTransfer() + } + + function computeDeployCollectiblesFee(subscriptionId, communityId, cKey, cChainId, cAccountAddress, cName, cSymbol, + cDescription, cSupply, cInfiniteSupply, cTransferable, cRemotelyDestruct, cArtworkSource, cArtworkCropRect) { + if (cKey !== "") + deleteToken(communityId, cKey) - const jsonArtworkFile = Utils.getImageAndCropInfoJson(collectibleItem.artworkSource, collectibleItem.artworkCropRect) - communityTokensModuleInst.deployCollectible(communityId, collectibleItem.accountAddress, collectibleItem.name, - collectibleItem.symbol, collectibleItem.description, collectibleItem.supply, - collectibleItem.infiniteSupply, collectibleItem.transferable, collectibleItem.remotelyDestruct, - collectibleItem.chainId, jsonArtworkFile) + const jsonArtworkFile = Utils.getImageAndCropInfoJson(cArtworkSource, cArtworkCropRect) + communityTokensModuleInst.computeDeployCollectiblesFee(subscriptionId, communityId, cAccountAddress, cName, + cSymbol, cDescription, cSupply, cInfiniteSupply, + cTransferable, cRemotelyDestruct, cChainId, jsonArtworkFile) } - function deployAsset(communityId, assetItem) { - if (assetItem.key !== "") - deleteToken(communityId, assetItem.key) + function computeDeployAssetsFee(subscriptionId, communityId, aKey, aChainId, aAccountAddress, aName, aSymbol, + aDescription, aSupply, aInfiniteSupply, aDecimals, aArtworkSource, aArtworkCropRect) { + if (aKey !== "") + deleteToken(communityId, aKey) - const jsonArtworkFile = Utils.getImageAndCropInfoJson(assetItem.artworkSource, assetItem.artworkCropRect) - communityTokensModuleInst.deployAssets(communityId, assetItem.accountAddress, assetItem.name, - assetItem.symbol, assetItem.description, assetItem.supply, - assetItem.infiniteSupply, assetItem.decimals, assetItem.chainId, jsonArtworkFile) + const jsonArtworkFile = Utils.getImageAndCropInfoJson(aArtworkSource, aArtworkCropRect) + communityTokensModuleInst.computeDeployAssetsFee(subscriptionId, communityId, aAccountAddress, aName, + aSymbol, aDescription, aSupply, + aInfiniteSupply, aDecimals, aChainId, jsonArtworkFile) } - function deployOwnerToken(communityId, ownerToken, tMasterToken) { - function deployOwnerTokenWithArtwork (communityId, artworkSource, ownerToken, tMasterToken) { - const jsonArtworkFile = Utils.getImageAndCropInfoJson(artworkSource, ownerToken.artworkCropRect) - communityTokensModuleInst.deployOwnerToken(communityId, ownerToken.accountAddress, ownerToken.name, ownerToken.symbol, ownerToken.description, - tMasterToken.name, tMasterToken.symbol, tMasterToken.description, ownerToken.chainId, jsonArtworkFile) + function computeDeployTokenOwnerFee(subscriptionId, communityId, + otChainId, otAccountAddress, otName, otSymbol, otDescription, otArtworkSource, otArtworkCropRect, + tmtName, tmtSymbol, tmtDescription) { + + function deployOwnerTokenWithArtwork (subscriptionId, communityId, artworkSource, + otChainId, otAccountAddress, otName, otSymbol, otDescription, otArtworkCropRect, + tmtName, tmtSymbol, tmtDescription) { + const jsonArtworkFile = Utils.getImageAndCropInfoJson(artworkSource, otArtworkCropRect) + communityTokensModuleInst.computeDeployTokenOwnerFee(subscriptionId, communityId, otAccountAddress, otName, otSymbol, otDescription, + tmtName, tmtSymbol, tmtDescription, otChainId, jsonArtworkFile) } - if (String(ownerToken.artworkSource).startsWith("https://localhost:")) { - const ownerTokenCopy = Object.assign({}, ownerToken) - const tMasterTokenCopy = Object.assign({}, tMasterToken) - Utils.fetchImageBase64(ownerToken.artworkSource, (dataUrl) => { - deployOwnerTokenWithArtwork(communityId, dataUrl, ownerTokenCopy, tMasterTokenCopy) + if (String(otArtworkSource).startsWith("https://localhost:")) { + Utils.fetchImageBase64(otArtworkSource, (dataUrl) => { + deployOwnerTokenWithArtwork(subscriptionId, communityId, dataUrl, + otChainId, otAccountAddress, otName, otSymbol, otDescription, otArtworkCropRect, + tmtName, tmtSymbol, tmtDescription) }) } else { - deployOwnerTokenWithArtwork(communityId, ownerToken.artworkSource, ownerToken, tMasterToken); + deployOwnerTokenWithArtwork(subscriptionId, communityId, otArtworkSource, + otChainId, otAccountAddress, otName, otSymbol, otDescription, otArtworkCropRect, + tmtName, tmtSymbol, tmtDescription) } } + function computeAirdropFee(subscriptionId, communityId, contractKeysAndAmounts, addresses, feeAccountAddress) { + communityTokensModuleInst.computeAirdropFee(subscriptionId, communityId, JSON.stringify(contractKeysAndAmounts), + JSON.stringify(addresses), feeAccountAddress) + } + function deleteToken(communityId, contractUniqueKey) { let parts = contractUniqueKey.split("_"); communityTokensModuleInst.removeCommunityToken(communityId, parts[0], parts[1]) @@ -108,17 +121,11 @@ QtObject { readonly property Connections connections: Connections { target: communityTokensModuleInst - function onDeployFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) { - root.deployFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) - } function onSelfDestructFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) { root.selfDestructFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) } - function onAirdropFeesUpdated(jsonFees) { - root.airdropFeeUpdated(JSON.parse(jsonFees)) - } function onBurnFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) { root.burnFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) @@ -128,17 +135,8 @@ QtObject { root.setSignerFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) } - function onDeploymentStateChanged(communityId, status, url) { - root.deploymentStateChanged(communityId, status, url) - } - function onOwnerTokenDeploymentStateChanged(communityId, status, url) { - root.ownerTokenDeploymentStateChanged(communityId, status, url) - } - function onOwnerTokenDeploymentStarted(communityId, url) { - root.ownerTokenDeploymentStarted(communityId, url) - } function onRemoteDestructStateChanged(communityId, tokenName, status, url) { root.remoteDestructStateChanged(communityId, tokenName, status, url) @@ -173,21 +171,17 @@ QtObject { } } + function stopUpdatesForSuggestedRoute() { + communityTokensModuleInst.stopUpdatesForSuggestedRoute() + } + // Burn: function computeBurnFee(tokenKey, amount, accountAddress, requestId) { console.assert(typeof amount === "string") communityTokensModuleInst.computeBurnFee(tokenKey, amount, accountAddress, requestId) } - function computeAirdropFee(communityId, contractKeysAndAmounts, addresses, feeAccountAddress, requestId) { - communityTokensModuleInst.computeAirdropFee( - communityId, JSON.stringify(contractKeysAndAmounts), - JSON.stringify(addresses), feeAccountAddress, requestId) - } - function computeDeployFee(communityId, chainId, accountAddress, tokenType, isOwnerDeployment, requestId) { - communityTokensModuleInst.computeDeployFee(communityId, chainId, accountAddress, tokenType, isOwnerDeployment, requestId) - } function computeSetSignerFee(chainId, contractAddress, accountAddress, requestId) { communityTokensModuleInst.computeSetSignerFee(chainId, contractAddress, accountAddress, requestId) @@ -223,10 +217,6 @@ QtObject { communityTokensModuleInst.burnTokens(communityId, tokenKey, burnAmount, accountAddress) } - // Airdrop tokens: - function airdrop(communityId, airdropTokens, addresses, feeAccountAddress) { - communityTokensModuleInst.airdropTokens(communityId, JSON.stringify(airdropTokens), JSON.stringify(addresses), feeAccountAddress) - } function asyncGetOwnerTokenDetails(communityId) { communityTokensModuleInst.asyncGetOwnerTokenDetails(communityId) diff --git a/ui/imports/shared/stores/CurrenciesStore.qml b/ui/imports/shared/stores/CurrenciesStore.qml index 2e4f37efa56..101d932cc43 100644 --- a/ui/imports/shared/stores/CurrenciesStore.qml +++ b/ui/imports/shared/stores/CurrenciesStore.qml @@ -76,4 +76,29 @@ QtObject { function getCurrentCurrencyAmount(amount) { return getCurrencyAmount(amount, currentCurrency) } + + function hexToDec(hex) { + return globalUtils.hexToDec(hex) + } + + function hexToEth(value) { + return hexToEthDenomination(value, "eth") + } + + function hexToEthDenomination(value, ethUnit) { + let BigOps = SQUtils.AmountsArithmetic + if (ethUnit !== "qwei" && ethUnit !== "eth") { + console.warn("unsuported conversion") + return BigOps.fromNumber(0) + } + let unitMapping = { + "gwei": 9, + "eth": 18 + } + let decValue = hexToDec(value) + if (!!decValue) { + return BigOps.div(BigOps.fromNumber(decValue), BigOps.fromNumber(1, unitMapping[ethUnit])) + } + return BigOps.fromNumber(0) + } } diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index f44a611dbc3..c3a9f862bcc 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -1009,6 +1009,13 @@ QtObject { ERC721Transfer, ERC1155Transfer, Swap, + CommunityBurn, + CommunityDeployAssets, + CommunityDeployCollectibles, + CommunityDeployOwnerToken, + CommunityMintTokens, + CommunityRemoteBurn, + CommunitySetSignerPubKey, Approve, Unknown } diff --git a/vendor/status-go b/vendor/status-go index d291204473d..78f74682ea6 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit d291204473d2dcdb27062f017ffe9966463d4818 +Subproject commit 78f74682ea6db2a6a3520de3b2e9afb6dade7380