From e9744f794df225517bba546d60ec6ae4e4e3f6aa Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Wed, 29 Jan 2025 13:11:50 +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. --- 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 | 201 ++--- .../main/communities/tokens/io_interface.nim | 110 +-- .../main/communities/tokens/module.nim | 433 ++++++---- .../modules/main/communities/tokens/view.nim | 120 +-- src/app/modules/main/controller.nim | 8 +- src/app/modules/main/io_interface.nim | 4 +- src/app/modules/main/module.nim | 123 ++- src/app/modules/main/view.nim | 17 + .../main/wallet_section/send/controller.nim | 13 +- .../main/wallet_section/send/io_interface.nim | 3 - .../modules/main/wallet_section/send/view.nim | 2 +- .../wallet_section/send_new/controller.nim | 3 - .../main/wallet_section/send_new/view.nim | 2 +- .../service/community_tokens/async_tasks.nim | 217 +---- .../dto/deployment_parameters.nim | 45 +- .../service/community_tokens/service.nim | 750 +++++++----------- 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 | 57 +- .../service/transaction/service.nim | 127 ++- src/backend/community_tokens.nim | 89 +-- src/backend/eth.nim | 66 +- src/backend/wallet.nim | 105 ++- .../Utils/SubscriptionBrokerCommunities.qml | 200 +++++ ui/StatusQ/src/StatusQ/Core/Utils/qmldir | 1 + ui/StatusQ/src/statusq.qrc | 1 + .../helpers/AirdropFeesSubscriber.qml | 35 +- .../helpers/DeployFeesSubscriber.qml | 4 + .../helpers/SetSignerFeesSubscriber.qml | 1 + .../helpers/SingleFeeSubscriber.qml | 30 +- .../helpers/TransactionFeesBroker.qml | 142 +++- .../panels/AirdropsSettingsPanel.qml | 10 +- .../panels/MintTokensSettingsPanel.qml | 53 +- .../Communities/popups/BurnTokensPopup.qml | 21 +- .../popups/FinaliseOwnershipPopup.qml | 25 +- .../popups/RemotelyDestructPopup.qml | 21 +- .../views/CommunitySettingsView.qml | 265 ++----- .../Communities/views/EditAirdropView.qml | 25 +- .../views/EditCommunityTokenView.qml | 33 +- .../Communities/views/MintedTokensView.qml | 8 +- .../dapps/internal/TransactionFeesBroker.qml | 2 - ui/app/mainui/AppMain.qml | 208 ++++- ui/app/mainui/Popups.qml | 29 +- ui/app/mainui/ToastsManager.qml | 48 -- .../shared/stores/CommunityTokensStore.qml | 176 ++-- ui/imports/shared/stores/CurrenciesStore.qml | 25 + ui/imports/utils/Constants.qml | 7 + vendor/status-go | 2 +- 53 files changed, 2186 insertions(+), 1737 deletions(-) create mode 100644 ui/StatusQ/src/StatusQ/Core/Utils/SubscriptionBrokerCommunities.qml diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index 6c412e83559..dc0c954d89c 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -215,7 +215,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, @@ -244,7 +245,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 7a2dc79cb6b..b74323d234f 100644 --- a/src/app/global/utils.nim +++ b/src/app/global/utils.nim @@ -47,6 +47,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..73c511dcc0f 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 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,59 @@ 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.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.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.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.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.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.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.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.onUserAuthenticated(args.password, args.pin) 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.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.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_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.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.onOwnerTokenOwnerAddress(args.chainId, args.contractAddress, args.address, args.addressName) 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,45 +112,35 @@ 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) - -proc burnTokens*(self: Controller, communityId: string, password: string, contractUniqueKey: string, amount: Uint256, addressFrom: string) = - self.communityTokensService.burnTokens(communityId, password, contractUniqueKey, amount, addressFrom) - -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 computeSetSignerFee*(self: Controller, uuid: string, communityId: string, chainId: int, contractAddress: string, addressFrom: string) = + self.communityTokensService.computeSetSignerFee(uuid, communityId, chainId, contractAddress, addressFrom) -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) +proc computeSelfDestructFee*(self: Controller, uuid: string, walletAndAmountList: seq[WalletAndAmount], contractUniqueKey: string, addressFrom: string) = + self.communityTokensService.computeSelfDestructFee(uuid, walletAndAmountList, contractUniqueKey, addressFrom) proc findContractByUniqueId*(self: Controller, contractUniqueKey: string): CommunityTokenDto = return self.communityTokensService.findContractByUniqueId(contractUniqueKey) -proc computeBurnFee*(self: Controller, contractUniqueKey: string, amount: Uint256, addressFrom: string, requestId: string) = - self.communityTokensService.computeBurnFee(contractUniqueKey, amount, addressFrom, requestId) +proc computeBurnFee*(self: Controller, uuid: string, contractUniqueKey: string, amount: string, addressFrom: string) = + self.communityTokensService.computeBurnFee(uuid, contractUniqueKey, amount, addressFrom) proc getNetworkByChainId*(self:Controller, chainId: int): NetworkItem = self.networksService.getNetworkByChainId(chainId) @@ -178,3 +159,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..8d09c061d9a 100644 --- a/src/app/modules/main/communities/tokens/io_interface.nim +++ b/src/app/modules/main/communities/tokens/io_interface.nim @@ -1,7 +1,9 @@ -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/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,110 +14,84 @@ 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.} = +method computeAirdropFee*(self: AccessInterface, uuid: string, 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.} = - raise newException(ValueError, "No implementation available") - -method selfDestructCollectibles*(self: AccessInterface, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: 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 burnTokens*(self: AccessInterface, communityId: string, contractUniqueKey: string, amount: string, addressFrom: 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 setSigner*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, addressFrom: string) {.base.} = - raise newException(ValueError, "No implementation available") +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 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.} = +method authenticateAndTransfer*(self: AccessInterface) {.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 deployOwnerToken*(self: AccessInterface, 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 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.} = - raise newException(ValueError, "No implementation available") - -method computeSetSignerFee*(self: AccessInterface, chainId: int, contractAddress: string, addressFrom: string, requestId: string) {.base.} = - raise newException(ValueError, "No implementation available") - -method computeSelfDestructFee*(self: AccessInterface, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string, requestId: string) {.base.} = - raise newException(ValueError, "No implementation available") - -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") - -method onAirdropFeesComputed*(self: AccessInterface, args: AirdropFeesArgs) {.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 onBurnFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) {.base.} = +method computeSetSignerFee*(self: AccessInterface, uuid: string, communityId: string, chainId: int, contractAddress: string, addressFrom: string) {.base.} = raise newException(ValueError, "No implementation available") -method onSetSignerFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) {.base.} = +method computeSelfDestructFee*(self: AccessInterface, uuid: string, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string) {.base.} = raise newException(ValueError, "No implementation available") -method onCommunityTokenDeployStateChanged*(self: AccessInterface, communityId: string, chainId: int, transactionHash: string, deployState: DeployState) {.base.} = +method computeBurnFee*(self: AccessInterface, uuid: string, contractUniqueKey: string, amount: string, addressFrom: string) {.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.} = +method removeCommunityToken*(self: AccessInterface, communityId: string, chainId: int, address: string) {.base.} = raise newException(ValueError, "No implementation available") -method onBurnStateChanged*(self: AccessInterface, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) {.base.} = +method refreshCommunityToken*(self: AccessInterface, chainId: int, address: string) {.base.} = raise newException(ValueError, "No implementation available") -method onAirdropStateChanged*(self: AccessInterface, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) {.base.} = +method onOwnerTokenReceived*(self: AccessInterface, communityId: string, communityName: string, chainId: int, contractAddress: string) {.base.} = raise newException(ValueError, "No implementation available") -method removeCommunityToken*(self: AccessInterface, communityId: string, chainId: int, address: string) {.base.} = +method onCommunityTokenReceived*(self: AccessInterface, name: string, symbol: string, image: string, communityId: string, communityName: string, balance: string, chainId: int, txHash: string, isFirst: bool, tokenType: int, accountName: string, accountAddress: string) {.base.} = raise newException(ValueError, "No implementation available") -method refreshCommunityToken*(self: AccessInterface, chainId: int, address: string) {.base.} = +method onLostOwnership*(self: AccessInterface, communityId: string) {.base.} = raise newException(ValueError, "No implementation available") -method onOwnerTokenReceived*(self: AccessInterface, communityId: string, communityName: string, chainId: int, contractAddress: string) {.base.} = +method declineOwnership*(self: AccessInterface, communityId: string) {.base.} = raise newException(ValueError, "No implementation available") -method onCommunityTokenReceived*(self: AccessInterface, name: string, symbol: string, image: string, communityId: string, communityName: string, balance: string, chainId: int, txHash: string, isFirst: bool, tokenType: int, accountName: string, accountAddress: string) {.base.} = +method onOwnerTokenOwnerAddress*(self: AccessInterface, chainId: int, contractAddress: string, address: string, addressName: string) {.base.} = raise newException(ValueError, "No implementation available") -method onSendOwnerTokenStateChanged*(self: AccessInterface, chainId: int, transactionHash: string, tokenName: string, status: ContractTransactionStatus) {.base.} = +method asyncGetOwnerTokenDetails*(self: AccessInterface, communityId: string) {.base.} = raise newException(ValueError, "No implementation available") -method onSetSignerStateChanged*(self: AccessInterface, communityId: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) {.base.} = +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 onLostOwnership*(self: AccessInterface, communityId: string) {.base.} = +method stopUpdatesForSuggestedRoute*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method declineOwnership*(self: AccessInterface, communityId: string) {.base.} = +method prepareSignaturesForTransactions*(self:AccessInterface, txForSigning: RouterTransactionsForSigningDto) {.base.} = raise newException(ValueError, "No implementation available") -method onOwnerTokenOwnerAddress*(self: AccessInterface, chainId: int, contractAddress: string, address: string, addressName: string) {.base.} = +method onTransactionSigned*(self: AccessInterface, keycardFlowType: string, keycardEvent: KeycardEvent) {.base.} = raise newException(ValueError, "No implementation available") -method asyncGetOwnerTokenDetails*(self: AccessInterface, communityId: string) {.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..3c16441a288 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,8 +36,12 @@ type controller: Controller view: View viewVariant: QVariant + tempUuid: string + tempPin: string + tempPassword: string tempTokenAndAmountList: seq[CommunityTokenAndAmount] tempWalletAndAmountList: seq[WalletAndAmount] + tempAddressPath: string tempAddressFrom: string tempCommunityId: string tempChainId: int @@ -44,19 +54,28 @@ 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,10 @@ method delete*(self: Module) = self.controller.delete method resetTempValues(self:Module) = + self.tempUuid = "" + self.tempPin = "" + self.tempPassword = "" + self.tempAddressPath = "" self.tempAddressFrom = "" self.tempCommunityId = "" self.tempDeploymentParams = DeploymentParameters() @@ -76,18 +99,191 @@ 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.tempAddressPath, 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.address + self.tempAddressPath = 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: + # no action required + return + if self.tempContractAction == ContractAction.SelfDestruct: + # no action required + return + if self.tempContractAction == ContractAction.Burn: + # no action required + return + if self.tempContractAction == ContractAction.DeployOwnerToken: + self.controller.storeDeployedOwnerContract(self.tempAddressFrom, chainId, txHash, self.tempOwnerDeploymentParams, self.tempMasterDeploymentParams) + return + if self.tempContractAction == ContractAction.SetSigner: + # no action required + return + error "Unknown contract action" proc getTokenAndAmountList(self: Module, communityId: string, tokensJsonString: string): seq[CommunityTokenAndAmount] = try: @@ -117,21 +313,16 @@ 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: + error "No tokens to airdrop" return - self.controller.computeAirdropFee(tokenAndAmountList, walletsJsonString.parseJson.to(seq[string]), addressFrom, requestId) + self.tempContractAction = ContractAction.Airdrop + 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 @@ -140,36 +331,16 @@ proc getWalletAndAmountListFromJson(self: Module, collectiblesToBurnJsonString: let amount = collectibleToBurn["amount"].getInt result.add(WalletAndAmount(walletAddress: walletAddress, amount: amount)) -method selfDestructCollectibles*(self: Module, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string) = - self.tempWalletAndAmountList = self.getWalletAndAmountListFromJson(collectiblesToBurnJsonString) - self.tempCommunityId = communityId - 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 - self.tempContractUniqueKey = contractUniqueKey - 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 - self.tempChainId = chainId - 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 +362,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 +387,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 +418,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,107 +426,39 @@ 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) - -method onAirdropFeesComputed*(self: Module, args: AirdropFeesArgs) = - self.view.updateAirdropFees(%args) - -method onBurnFeeComputed*(self: Module, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode, responseId: string) = - self.view.updateBurnFee(ethCurrency, fiatCurrency, errorCode.int, responseId) - -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) +method computeSetSignerFee*(self: Module, uuid: string, communityId: string, chainId: int, contractAddress: string, addressFrom: string) = + self.tempContractAction = ContractAction.SetSigner + self.tempUuid = uuid + self.tempAddressFrom = addressFrom + self.tempCommunityId = communityId + self.controller.computeSetSignerFee(uuid, communityId, chainId, contractAddress, addressFrom) -method computeSelfDestructFee*(self: Module, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string, requestId: string) = +method computeSelfDestructFee*(self: Module, uuid: string, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string) = let walletAndAmountList = self.getWalletAndAmountListFromJson(collectiblesToBurnJsonString) - self.controller.computeSelfDestructFee(walletAndAmountList, contractUniqueKey, addressFrom, requestId) - -method computeBurnFee*(self: Module, contractUniqueKey: string, amount: string, addressFrom: string, requestId: string) = - self.controller.computeBurnFee(contractUniqueKey, amount.parse(Uint256), addressFrom, requestId) + if len(walletAndAmountList) == 0: + error "No collectibles/assets to burn" + return + self.tempContractAction = ContractAction.SelfDestruct + self.tempUuid = uuid + self.tempAddressFrom = addressFrom + self.controller.computeSelfDestructFee(uuid, walletAndAmountList, contractUniqueKey, addressFrom) -proc createUrl(self: Module, chainId: int, transactionHash: string): string = - let network = self.controller.getNetworkByChainId(chainId) - result = if network != nil: network.blockExplorerURL & "/tx/" & transactionHash else: "" +method computeBurnFee*(self: Module, uuid: string, contractUniqueKey: string, amount: string, addressFrom: string) = + self.tempContractAction = ContractAction.Burn + self.tempUuid = uuid + self.tempAddressFrom = addressFrom + self.controller.computeBurnFee(uuid, contractUniqueKey, amount, addressFrom) 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) - self.view.emitRemoteDestructStateChanged(communityId, tokenName, status.int, url) - -method onBurnStateChanged*(self: Module, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) = - let url = self.createUrl(chainId, transactionHash) - self.view.emitBurnStateChanged(communityId, tokenName, status.int, url) - -method onAirdropStateChanged*(self: Module, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) = - let url = self.createUrl(chainId, transactionHash) - let chainName = self.getChainName(chainId) - self.view.emitAirdropStateChanged(communityId, tokenName, chainName, status.int, url) - method onOwnerTokenReceived*(self: Module, communityId: string, communityName: string, chainId: int, contractAddress: string) = self.view.emitOwnerTokenReceived(communityId, communityName, chainId, contractAddress) method onCommunityTokenReceived*(self: Module, name: string, symbol: string, image: string, communityId: string, communityName: string, balance: string, chainId: int, txHash: string, isFirst: bool, tokenType: int, accountName: string, accountAddress: string) = self.view.emitCommunityTokenReceived(name, symbol, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, accountName, accountAddress) -method onSetSignerStateChanged*(self: Module, communityId: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) = - let communityDto = self.controller.getCommunityById(communityId) - let communityName = communityDto.name - let url = self.createUrl(chainId, transactionHash) - self.view.emitSetSignerStateChanged(communityId, communityName, status.int, url) - -method onSendOwnerTokenStateChanged*(self: Module, chainId: int, transactionHash: string, tokenName: string, status: ContractTransactionStatus) = - let url = self.createUrl(chainId, transactionHash) - self.view.emitSendOwnerTokenStateChanged(tokenName, status.int, url) - method onLostOwnership*(self: Module, communityId: string) = let communityDto = self.controller.getCommunityById(communityId) let communityName = communityDto.name @@ -393,3 +491,18 @@ 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..ab6145eb2d6 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,101 +47,35 @@ 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 selfDestructCollectibles*(self: View, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string) {.slot.} = - self.communityTokensModule.selfDestructCollectibles(communityId, collectiblesToBurnJsonString, contractUniqueKey, addressFrom) + proc computeAirdropFee*(self: View, uuid: string, communityId: string, tokensJsonString: string, + walletsJsonString: string, addressFrom: string) {.slot.} = + self.communityTokensModule.computeAirdropFee(uuid, communityId, tokensJsonString, walletsJsonString, addressFrom) - proc burnTokens*(self: View, communityId: string, contractUniqueKey: string, amount: string, addressFrom: string) {.slot.} = - self.communityTokensModule.burnTokens(communityId, contractUniqueKey, amount, addressFrom) - - 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.} - proc setSignerFeeUpdated*(self: View, ethCurrency: QVariant, fiatCurrency: QVariant, errorCode: int, responseId: string) {.signal.} proc ownerTokenReceived*(self: View, communityId: string, communityName: string, chainId: int, contractAddress: string) {.signal.} proc communityTokenReceived*(self: View, name: string, symbol: string, image: string, communityId: string, communityName: string, balance: string, chainId: int, txHash: string, isFirst: bool, tokenType: int, accountName: string, accountAddress: string) {.signal.} - proc setSignerStateChanged*(self: View, communityId: string, communityName: string, status: int, url: string) {.signal.} 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) + proc computeSetSignerFee*(self: View, uuid: string, communityId: string, chainId: int, contractAddress: string, addressFrom: string) {.slot.} = + self.communityTokensModule.computeSetSignerFee(uuid, communityId, chainId, contractAddress, addressFrom) - proc computeSelfDestructFee*(self: View, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string, requestId: string) {.slot.} = - self.communityTokensModule.computeSelfDestructFee(collectiblesToBurnJsonString, contractUniqueKey, addressFrom, requestId) + proc computeSelfDestructFee*(self: View, uuid: string, collectiblesToBurnJsonString: string, contractUniqueKey: string, addressFrom: string) {.slot.} = + self.communityTokensModule.computeSelfDestructFee(uuid, collectiblesToBurnJsonString, contractUniqueKey, addressFrom) - proc computeBurnFee*(self: View, contractUniqueKey: string, amount: string, addressFrom: string, requestId: string) {.slot.} = - self.communityTokensModule.computeBurnFee(contractUniqueKey, amount, addressFrom, requestId) + proc computeBurnFee*(self: View, uuid: string, contractUniqueKey: string, amount: string, addressFrom: string) {.slot.} = + self.communityTokensModule.computeBurnFee(uuid, contractUniqueKey, amount, addressFrom) 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) - - proc updateSetSignerFee*(self: View, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: int, responseId: string) = - self.setSignerFeeUpdated(newQVariant(ethCurrency), newQVariant(fiatCurrency), errorCode, responseId) - - proc updateSelfDestructFee*(self: View, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: int, responseId: string) = - self.selfDestructFeeUpdated(newQVariant(ethCurrency), newQVariant(fiatCurrency), errorCode, responseId) - - 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) = - self.remoteDestructStateChanged(communityId, tokenName, status, url) - - proc burnStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) {.signal.} - proc emitBurnStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) = - self.burnStateChanged(communityId, tokenName, status, url) - - proc airdropStateChanged*(self: View, communityId: string, tokenName: string, chainName: string, status: int, url: string) {.signal.} - 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) proc emitCommunityTokenReceived*(self: View, name: string, symbol: string, image: string, communityId: string, communityName: string, balance: string, chainId: int, txHash: string, isFirst: bool, tokenType: int, accountName: string, accountAddress: string) = self.communityTokenReceived(name, symbol, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, accountName, accountAddress) - proc emitSetSignerStateChanged*(self: View, communityId: string, communityName: string, status: int, url: string) = - self.setSignerStateChanged(communityId, communityName, status, url) - proc emitOwnershipLost*(self: View, communityId: string, communityName: string) = self.ownershipNodeLost(communityId, communityName) - proc emitSendOwnerTokenStateChanged*(self: View, tokenName: string, status: int, url: string) = - self.sendOwnerTokenStateChanged(tokenName, status, url) - proc asyncGetOwnerTokenDetails*(self: View, communityId: string) {.slot.} = self.communityTokensModule.asyncGetOwnerTokenDetails(communityId) @@ -145,3 +89,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 7c082fba44a..8b2ad5497ef 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 f217f2231bd..ed3f3e44c36 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,28 @@ proc sendNotification[T](self: Module[T], status: string, sendDetails: SendDetai error = "" txHash = "" isApprovalTx = false + #community specific details + # main community details + communityId = "" + communityName = "" + communityInvolvedTokens = 0 + communityTotalAmount = "" + # community token 1 details + communityAmount1 = "" + communityAmountInfinite1 = false + communityAssetName1 = "" + communityAssetDecimals1 = 0 + # community token 2 details + communityAmount2 = "" + communityAmountInfinite2 = false + communityAssetName2 = "" + communityAssetDecimals2 = 0 + # other community details + communityInvolvedAddress = "" + communityNubmerOfInvolvedAddresses = 0 + communityOwnerTokenName = "" + communityMasterTokenName = "" + communityDeployedTokenName = "" if not sendDetails.errorResponse.isNil: error = sendDetails.errorResponse.details @@ -503,6 +527,64 @@ 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: + let getTokenContractAddressAtIndex = proc(transferDetails: seq[TansferDetails], index: int): string = + if transferDetails.len == 0 or index >= transferDetails.len: + return "" + return transferDetails[index].tokenContractAddress + + let getCommunityAmount = proc(transferDetails: seq[TansferDetails]): string = + var total: UInt256 = stint.u256(0) + for td in transferDetails: + total += td.amount + return total.toString(10) + + communityId = sendDetails.communityParams.communityId + let item = self.view.model().getItemById(communityId) + if item.id == "": + error = "cannot_resolve_community" + else: + communityName = item.name + + communityInvolvedTokens = sendDetails.communityParams.transferDetails.len + communityTotalAmount = getCommunityAmount(sendDetails.communityParams.transferDetails) + + for i in 0 ..< sendDetails.communityParams.transferDetails.len: + let tokenContractAddress = getTokenContractAddressAtIndex(sendDetails.communityParams.transferDetails, i) + if tokenContractAddress.len == 0: + error = "cannot_resolve_token_contract_address" + else: + let communityToken = self.communityTokensService.getCommunityTokenFromCache(fromChain, tokenContractAddress) + if communityToken.name.len == 0: + error = "cannot_resolve_community_token" + else: + if i == 0: + communityAssetName1 = communityToken.name + communityAssetDecimals1 = communityToken.decimals + communityAmount1 = sendDetails.communityParams.transferDetails[i].amount.toString(10) + elif i == 1: + communityAssetName2 = communityToken.name + communityAssetDecimals2 = communityToken.decimals + communityAmount2 = sendDetails.communityParams.transferDetails[i].amount.toString(10) + + if txType == SendType.CommunityDeployOwnerToken: + communityOwnerTokenName = sendDetails.communityParams.ownerTokenParameters.name + communityMasterTokenName = sendDetails.communityParams.masterTokenParameters.name + + if txType == SendType.CommunityDeployAssets or + txType == SendType.CommunityDeployCollectibles: + communityDeployedTokenName = sendDetails.communityParams.deploymentParameters.name + communityAmountInfinite1 = sendDetails.communityParams.deploymentParameters.infiniteSupply + if not communityAmountInfinite1: + communityAmount1 = sendDetails.communityParams.deploymentParameters.supply.toString(10) + + if txType == SendType.CommunityRemoteBurn or + txType == SendType.CommunityBurn: + communityNubmerOfInvolvedAddresses = sendDetails.communityParams.walletAddresses.len + if communityNubmerOfInvolvedAddresses == 1: # set address only if all tokens are from the same address + communityInvolvedAddress = sendDetails.communityParams.walletAddresses[0] + self.view.showTransactionToast( sendDetails.uuid, sendDetails.sendType, @@ -523,6 +605,23 @@ proc sendNotification[T](self: Module[T], status: string, sendDetails: SendDetai sendDetails.username, sendDetails.publicKey, sendDetails.packId, + communityId, + communityName, + communityInvolvedTokens, + communityTotalAmount, + communityAmount1, + communityAmountInfinite1, + communityAssetName1, + communityAssetDecimals1, + communityAmount2, + communityAmountInfinite2, + communityAssetName2, + communityAssetDecimals2, + communityInvolvedAddress, + communityNubmerOfInvolvedAddresses, + communityOwnerTokenName, + communityMasterTokenName, + communityDeployedTokenName, status, error, ) @@ -1348,16 +1447,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..01410cca1e5 100644 --- a/src/app/modules/main/view.nim +++ b/src/app/modules/main/view.nim @@ -361,6 +361,23 @@ QtObject: username: string, publicKey: string, packId: string, + communityId: string, + communityName: string, + communityInvolvedTokens: int, + communityTotalAmount: string, + communityAmount1: string, + communityAmountInfinite1: bool, + communityAssetName1: string, + communityAssetDecimals1: int, + communityAmount2: string, + communityAmountInfinite2: bool, + communityAssetName2: string, + communityAssetDecimals2: int, + communityInvolvedAddress: string, + communityNubmerOfInvolvedAddresses: int, + communityOwnerTokenName: string, + communityMasterTokenName: string, + communityDeployedTokenName: 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..2087d624091 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: @@ -69,9 +69,6 @@ proc init*(self: Controller) = if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: "" ) - self.events.on(SIGNAL_OWNER_TOKEN_SENT) do(e:Args): - let args = OwnerTokenSentArgs(e) - self.delegate.transactionWasSent(args.uuid, args.chainId, approvalTx = false, args.txHash, error = "") self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args): let args = SharedKeycarModuleArgs(e) @@ -81,6 +78,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/modules/main/wallet_section/send_new/controller.nim b/src/app/modules/main/wallet_section/send_new/controller.nim index 21b8bb7003a..1ca24d3fc43 100644 --- a/src/app/modules/main/wallet_section/send_new/controller.nim +++ b/src/app/modules/main/wallet_section/send_new/controller.nim @@ -63,9 +63,6 @@ proc init*(self: Controller) = if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: "" ) - self.events.on(SIGNAL_OWNER_TOKEN_SENT) do(e:Args): - let args = OwnerTokenSentArgs(e) - self.delegate.transactionWasSent(args.uuid, args.chainId, approvalTx = false, args.txHash, error = "") self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args): let args = SharedKeycarModuleArgs(e) diff --git a/src/app/modules/main/wallet_section/send_new/view.nim b/src/app/modules/main/wallet_section/send_new/view.nim index 6f57a4ec725..d2bef8d5a7c 100644 --- a/src/app/modules/main/wallet_section/send_new/view.nim +++ b/src/app/modules/main/wallet_section/send_new/view.nim @@ -4,7 +4,7 @@ import ./io_interface, ./path_model, ./path_item import app_service/common/utils as common_utils import app_service/service/eth/utils as eth_utils import app_service/service/transaction/dto as transaction_dto -from backend/eth import ExtraKeyPackId +from backend/wallet import ExtraKeyPackId export path_model 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 94d9311f46f..7511e2ac6fe 100644 --- a/src/app_service/service/community_tokens/service.nim +++ b/src/app_service/service/community_tokens/service.nim @@ -1,34 +1,37 @@ 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 -from backend/collectibles_types import CollectibleOwner -import ../../../backend/backend -import ../../../backend/response_type +import app/global/global_singleton +import app/core/eventemitter +import app/core/tasks/[qt, threadpool] +import app/core/signals/types -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 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 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/types +import app_service/common/account_constants +import app_service/common/utils as common_utils import ./community_collectible_owner import ./dto/deployment_parameters @@ -39,10 +42,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" @@ -63,20 +66,17 @@ type transactionHash*: string deployState*: DeployState -type - CommunityTokensArgs* = ref object of Args - communityTokens*: seq[CommunityTokenDto] - 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 @@ -104,52 +104,6 @@ type transactionHash*: string status*: ContractTransactionStatus -type - ComputeFeeArgs* = ref object of Args - ethCurrency*: CurrencyAmount - fiatCurrency*: CurrencyAmount - errorCode*: ComputeFeeErrorCode - contractUniqueKey*: string # used for minting - requestId*: string - -type - SetSignerArgs* = ref object of Args - transactionHash*: string - status*: ContractTransactionStatus - communityId*: string - chainId*: int - -proc `%`*(self: ComputeFeeArgs): JsonNode = - result = %* { - "ethFee": if self.ethCurrency == nil: newCurrencyAmount().toJsonNode() else: self.ethCurrency.toJsonNode(), - "fiatFee": if self.fiatCurrency == nil: newCurrencyAmount().toJsonNode() else: self.fiatCurrency.toJsonNode(), - "errorCode": self.errorCode.int, - "contractUniqueKey": self.contractUniqueKey, - } - -proc computeFeeArgsToJsonArray(args: seq[ComputeFeeArgs]): JsonNode = - let arr = newJArray() - for arg in args: - arr.elems.add(%arg) - return arr - -type - AirdropFeesArgs* = ref object of Args - fees*: seq[ComputeFeeArgs] - totalEthFee*: CurrencyAmount - totalFiatFee*: CurrencyAmount - errorCode*: ComputeFeeErrorCode - requestId*: string - -proc `%`*(self: AirdropFeesArgs): JsonNode = - result = %* { - "fees": computeFeeArgsToJsonArray(self.fees), - "totalEthFee": if self.totalEthFee == nil: newCurrencyAmount().toJsonNode() else: self.totalEthFee.toJsonNode(), - "totalFiatFee": if self.totalFiatFee == nil: newCurrencyAmount().toJsonNode() else: self.totalFiatFee.toJsonNode(), - "errorCode": self.errorCode.int, - "requestId": self.requestId, - } - type CommunityTokenOwnersArgs* = ref object of Args communityId*: string @@ -253,12 +207,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_COMPUTE_SET_SIGNER_FEE* = "communityTokens-computeSetSignerFee" -const SIGNAL_COMPUTE_SELF_DESTRUCT_FEE* = "communityTokens-computeSelfDestructFee" -const SIGNAL_COMPUTE_BURN_FEE* = "communityTokens-computeBurnFee" -const SIGNAL_COMPUTE_AIRDROP_FEE* = "communityTokens-computeAirdropFee" +const SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STORED* = "communityTokens-communityTokenDeploymentStored" const SIGNAL_COMMUNITY_TOKEN_OWNERS_FETCHED* = "communityTokens-communityTokenOwnersFetched" const SIGNAL_COMMUNITY_TOKEN_OWNERS_LOADING_FAILED* = "communityTokens-communityTokenOwnersLoadingFailed" const SIGNAL_REMOTE_DESTRUCT_STATUS* = "communityTokens-communityTokenRemoteDestructStatus" @@ -268,10 +217,9 @@ 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" const SIGNAL_FINALISE_OWNERSHIP_STATUS* = "communityTokens-finaliseOwnershipStatus" const SIGNAL_OWNER_TOKEN_OWNER_ADDRESS* = "communityTokens-ownerTokenOwnerAddress" const SIGNAL_COMMUNITY_TOKEN_RECEIVED* = "communityTokens-communityTokenReceived" @@ -281,6 +229,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 @@ -314,7 +263,6 @@ QtObject: proc restartTokenHoldersTimer(self: Service, chainId: int, contractAddress: string) proc refreshTokenHolders(self: Service, token: CommunityTokenDto) - proc delete*(self: Service) = delete(self.tokenHoldersTimer) self.QObject.delete @@ -322,6 +270,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 +283,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 +307,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]) = @@ -486,11 +441,6 @@ QtObject: else: error "Signer not set" - let data = SetSignerArgs(status: if signalArgs.success: ContractTransactionStatus.Completed else: ContractTransactionStatus.Failed, - chainId: chainId, - transactionHash: signalArgs.hash, - communityId: communityId) - self.events.emit(SIGNAL_SET_SIGNER_STATUS, data) # TODO move AC notifications to status-go let response = if signalArgs.success: tokens_backend.registerReceivedOwnershipNotification(communityId) else: tokens_backend.registerSetSignerFailedNotification(communityId) @@ -604,17 +554,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 +581,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 +777,41 @@ 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[uint](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 = "", + tokenIds = @[], + 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 + ) + ) proc getFiatValue(self: Service, cryptoBalance: float, cryptoSymbol: string): float = if (cryptoSymbol == ""): @@ -879,62 +824,102 @@ QtObject: for token in allTokens: if common_utils.contractUniqueKey(token.chainId, token.address) == contractUniqueKey: return token + raise newException(CatchableError, "Contract not found") - 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[uint](self.vptr), - slot: "onDeployFees", - chainId: chainId, - addressFrom: accountAddress, - tokenType: tokenType, - requestId: requestId + self.transactionService.suggestedCommunityRoutes( + uuid, + sendType, + chainId, + accountFrom, + communityId, + signerPubKey = "", + tokenIds = @[], + 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 + error "Error loading deploy owner fees", msg = e.msg + self.transactionService.emitSuggestedRoutesReadySignal( + SuggestedRoutesArgs( + uuid: uuid, + sendType: sendType, + errCode: $InternalErrorCode, + errDescription: e.msg + ) + ) - proc computeSetSignerFee*(self: Service, chainId: int, contractAddress: string, accountAddress: string, requestId: string) = + proc computeSetSignerFee*(self: Service, uuid: string, communityId: string, chainId: int, contractAddress: string, addressFrom: string) = + let sendType = SendType.CommunitySetSignerPubKey try: - let arg = AsyncSetSignerFeesArg( - tptr: asyncSetSignerFeesTask, - vptr: cast[uint](self.vptr), - slot: "onSetSignerFees", - chainId: chainId, - contractAddress: contractAddress, - addressFrom: accountAddress, - requestId: requestId, - newSignerPubKey: singletonInstance.userProfile.getPubKey() + var transferDetails: seq[JsonNode] + transferDetails.add(%* { + "tokenContractAddress": contractAddress, + }) + + self.transactionService.suggestedCommunityRoutes( + uuid, + sendType, + chainId, + addressFrom, + communityId, + signerPubKey = singletonInstance.userProfile.getPubKey(), + tokenIds = @[], + walletAddresses = @[], + transferDetails ) - self.threadpool.start(arg) except Exception as e: - #TODO: handle error - emit error signal - error "Error loading fees", msg = e.msg - + error "Error loading burn fees", msg = e.msg + self.transactionService.emitSuggestedRoutesReadySignal( + SuggestedRoutesArgs( + uuid: uuid, + sendType: sendType, + errCode: $InternalErrorCode, + errDescription: e.msg + ) + ) - proc computeDeployOwnerContractsFee*(self: Service, chainId: int, accountAddress: string, communityId: string, ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters, requestId: string) = + proc computeDeployOwnerContractsFee*(self: Service, uuid: string, chainId: int, accountFrom: string, communityId: string, + ownerDeploymentParams: DeploymentParameters, masterDeploymentParams: DeploymentParameters) = try: - let arg = AsyncDeployOwnerContractsFeesArg( - tptr: asyncGetDeployOwnerContractsFeesTask, - vptr: cast[uint](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(), + tokenIds = @[], + 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: @@ -955,56 +940,56 @@ QtObject: proc getTokensToBurn(self: Service, walletAndAmountList: seq[WalletAndAmount], contract: CommunityTokenDto): seq[Uint256] = if contract.address == "": - error "Can't find contract" - return + raise newException(CatchableError, "contract address is empty") let tokenOwners = self.getCommunityTokenOwners(contract.communityId, contract.chainId, contract.address) let tokenIds = self.collectTokensToBurn(walletAndAmountList, tokenOwners) if len(tokenIds) == 0: - error "Can't find token ids to burn" + raise newException(CatchableError, "cannot resolve token ids to burn") 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 + proc computeSelfDestructFee*(self: Service, uuid: string, walletAndAmountList: seq[WalletAndAmount], contractUniqueKey: string, addressFrom: string) = - proc computeSelfDestructFee*(self: Service, walletAndAmountList: seq[WalletAndAmount], contractUniqueKey: string, addressFrom: string, requestId: string) = + let sendType = SendType.CommunityRemoteBurn try: + if walletAndAmountList.len == 0: + raise newException(CatchableError, "no amounts to burn for addresses") 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[uint](self.vptr), - slot: "onSelfDestructFees", - chainId: contract.chainId, - contractAddress: contract.address, - tokenIds: tokenIds, - addressFrom: addressFrom, - requestId: requestId + let tokensCount = len(tokenIds) + if tokensCount == 0: + raise newException(CatchableError, "token list is empty") + + let + bigTokensCount = common_utils.stringToUint256($tokensCount) + hexTokensCount = "0x" & eth_utils.stripLeadingZeros(bigTokensCount.toHex) + + var transferDetails: seq[JsonNode] + transferDetails.add(%* { + "tokenContractAddress": contract.address, + "amount": hexTokensCount, + }) + + self.transactionService.suggestedCommunityRoutes( + uuid, + sendType, + contract.chainId, + addressFrom, + contract.communityId, + signerPubKey = "", + tokenIds.map(x => "0x" & eth_utils.stripLeadingZeros(x.toHex)), + walletAddresses = walletAndAmountList.map(x => x.walletAddress), + transferDetails ) - self.threadpool.start(arg) except Exception as e: - error "Error loading fees", msg = e.msg + error "Error loading self destruct fees", msg = e.msg + self.transactionService.emitSuggestedRoutesReadySignal( + SuggestedRoutesArgs( + uuid: uuid, + sendType: sendType, + errCode: $InternalErrorCode, + errDescription: e.msg + ) + ) proc create0CurrencyAmounts(self: Service): (CurrencyAmount, CurrencyAmount) = let ethCurrency = newCurrencyAmount(0.0, ethSymbol, 1, false) @@ -1024,83 +1009,42 @@ QtObject: errorCode = ComputeFeeErrorCode.Revert 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 - - 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 - - proc computeBurnFee*(self: Service, contractUniqueKey: string, amount: Uint256, addressFrom: string, requestId: string) = + proc computeBurnFee*(self: Service, uuid: string, contractUniqueKey: string, amount: string, addressFrom: string) = + let sendType = SendType.CommunityBurn try: let contract = self.findContractByUniqueId(contractUniqueKey) - let arg = AsyncGetBurnFees( - tptr: asyncGetBurnFeesTask, - vptr: cast[uint](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) + let + bigAmount = common_utils.stringToUint256(amount) + hexAmount = "0x" & eth_utils.stripLeadingZeros(bigAmount.toHex) - # Returns eth value with l1 fee included - proc computeEthValue(self:Service, gasUnits: int, suggestedFees: SuggestedFeesDto): float = - try: - let maxFees = suggestedFees.maxFeePerGasM - let gasPrice = if suggestedFees.eip1559Enabled: maxFees else: suggestedFees.gasPrice + var transferDetails: seq[JsonNode] + transferDetails.add(%* { + "tokenContractAddress": contract.address, + "amount": hexAmount, + }) - let weiValue = gwei2Wei(gasPrice) * gasUnits.u256 - let l1FeeInWei = gwei2Wei(suggestedFees.l1GasFee) - let ethValueStr = wei2Eth(weiValue + l1FeeInWei) - return parseFloat(ethValueStr) + self.transactionService.suggestedCommunityRoutes( + uuid, + sendType, + contract.chainId, + addressFrom, + contract.communityId, + signerPubKey = "", + tokenIds = @[], + walletAddresses = @[], + transferDetails + ) except Exception as e: - error "Error computing eth value", msg = e.msg + error "Error loading burn fees", msg = e.msg + self.transactionService.emitSuggestedRoutesReadySignal( + SuggestedRoutesArgs( + uuid: uuid, + sendType: sendType, + errCode: $InternalErrorCode, + errDescription: e.msg + ) + ) proc getWalletBalanceForChain(self:Service, walletAddress: string, chainId: int): float = var balance = 0.0 @@ -1114,18 +1058,6 @@ 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 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] = try: @@ -1146,118 +1078,6 @@ 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 isTokenDeployed(self: Service, token: CommunityTokenDto): bool = return token.deployState == DeployState.Deployed @@ -1440,4 +1260,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 a62622e0186..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 65cc6fa5be5..4273c567892 100644 --- a/src/app_service/service/transaction/dtoV2.nim +++ b/src/app_service/service/transaction/dtoV2.nim @@ -32,9 +32,11 @@ type amountOut*: UInt256 suggestedLevelsForMaxFeesPerGas*: SuggestedLevelsForMaxFeesPerGasDto + maxFeesPerGas*: UInt256 suggestedMinPriorityFee*: UInt256 suggestedMaxPriorityFee*: UInt256 currentBaseFee*: UInt256 + usedContractAddress*: string txNonce*: UInt256 txMaxFeesPerGas*: UInt256 @@ -85,9 +87,11 @@ proc toTransactionPathDtoV2*(jsonObj: JsonNode): TransactionPathDtoV2 = discard jsonObj.getProp("AmountInLocked", result.amountInLocked) result.amountOut = stint.fromHex(UInt256, jsonObj{"AmountOut"}.getStr) result.suggestedLevelsForMaxFeesPerGas = jsonObj["SuggestedLevelsForMaxFeesPerGas"].toSuggestedLevelsForMaxFeesPerGasDto() + result.maxFeesPerGas = stint.fromHex(UInt256, jsonObj{"MaxFeesPerGas"}.getStr) result.suggestedMinPriorityFee = stint.fromHex(UInt256, jsonObj{"SuggestedMinPriorityFee"}.getStr) result.suggestedMaxPriorityFee = stint.fromHex(UInt256, jsonObj{"SuggestedMaxPriorityFee"}.getStr) result.currentBaseFee = stint.fromHex(UInt256, jsonObj{"CurrentBaseFee"}.getStr) + discard jsonObj.getProp("UsedContractAddress", result.usedContractAddress) result.txNonce = stint.fromHex(UInt256, jsonObj{"TxNonce"}.getStr) result.txMaxFeesPerGas = stint.fromHex(UInt256, jsonObj{"TxMaxFeesPerGas"}.getStr) result.txBaseFee = stint.fromHex(UInt256, jsonObj{"TxBaseFee"}.getStr) diff --git a/src/app_service/service/transaction/router_transactions_dto.nim b/src/app_service/service/transaction/router_transactions_dto.nim index b324545619e..59bf80a7200 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,26 @@ type ErrorResponse* = ref object details*: string code*: string + +type + TansferDetails* = ref object + tokenType*: int + privilegeLevel*: int + tokenContractAddress*: string + amount*: UInt256 + +type + CommunityParamsDto* = ref object + communityID*: string + signerPubKey*: string + tokenIds*: seq[UInt256] + walletAddresses*: seq[string] + tokenDeploymentSignature*: string + ownerTokenParameters*: DeploymentParameters + masterTokenParameters*: DeploymentParameters + deploymentParameters*: DeploymentParameters + transferDetails*: seq[TansferDetails] + type SendDetailsDto* = ref object uuid*: string @@ -29,6 +50,7 @@ type username*: string publicKey*: string packId*: string + communityParams*: CommunityParamsDto type SigningDetails* = ref object @@ -77,6 +99,37 @@ proc toErrorResponse*(jsonObj: JsonNode): ErrorResponse = if jsonObj.contains("code"): result.code = jsonObj["code"].getStr +proc toTansferDetails*(jsonObj: JsonNode): TansferDetails = + result = TansferDetails() + discard jsonObj.getProp("tokenType", result.tokenType) + discard jsonObj.getProp("privilegeLevel", result.privilegeLevel) + discard jsonObj.getProp("tokenContractAddress", result.tokenContractAddress) + var tmpObj: JsonNode + if jsonObj.getProp("amount", tmpObj): + result.amount = stint.fromHex(UInt256, tmpObj.getStr) + +proc toCommunityParamsDto*(jsonObj: JsonNode): CommunityParamsDto = + result = CommunityParamsDto() + discard jsonObj.getProp("communityID", result.communityID) + discard jsonObj.getProp("signerPubKey", result.signerPubKey) + discard jsonObj.getProp("tokenDeploymentSignature", result.tokenDeploymentSignature) + var tmpObj: JsonNode + 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) + if jsonObj.getProp("transferDetails", tmpObj) and tmpObj.kind == JArray: + for tx in tmpObj: + result.transferDetails.add(toTansferDetails(tx)) + proc toSendDetailsDto*(jsonObj: JsonNode): SendDetailsDto = result = SendDetailsDto() discard jsonObj.getProp("uuid", result.uuid) @@ -100,6 +153,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 ec169758ef4..9521e66975f 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 @@ -43,10 +45,9 @@ const SIGNAL_SUGGESTED_ROUTES_READY* = "suggestedRoutesReady" const SIGNAL_HISTORY_NON_ARCHIVAL_NODE* = "historyNonArchivalNode" const SIGNAL_HISTORY_ERROR* = "historyError" 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,11 +115,16 @@ type type SuggestedRoutesArgs* = ref object of Args uuid*: string + sendType*: SendType suggestedRoutes*: SuggestedRoutesDto # this should be the only one used when old send modal code is removed routes*: seq[TransactionPathDtoV2] errCode*: string errDescription*: string + # Below fields used for community related tx + costPerPath*: seq[CostPerPath] + totalCostEthCurrency*: CurrencyAmount + totalCostFiatCurrency*: CurrencyAmount type TransactionDecodedArgs* = ref object of Args @@ -139,10 +145,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) @@ -154,6 +161,7 @@ QtObject: proc newService*( events: EventEmitter, threadpool: ThreadPool, + currencyService: currency_service.Service, networkService: network_service.Service, settingsService: settings_service.Service, tokenService: token_service.Service, @@ -162,6 +170,7 @@ QtObject: result.QObject.setup result.events = events result.threadpool = threadpool + result.currencyService = currencyService result.networkService = networkService result.settingsService = settingsService result.tokenService = tokenService @@ -325,20 +334,11 @@ QtObject: return for tx in sentTransactions: - if sendDetails.ownerTokenBeingSent: - self.events.emit(SIGNAL_OWNER_TOKEN_SENT, OwnerTokenSentArgs( - chainId: tx.fromChain, - txHash: tx.hash, - uuid: sendDetails.uuid, - tokenName: tx.fromToken, - status: ContractTransactionStatus.InProgress - )) - else: - self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionArgs( - status: TxStatusSending, # here should be TxStatusPending state, but that's not what Figma wants - sendDetails: sendDetails, - sentTransaction: tx - )) + self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionArgs( + status: TxStatusSending, # here should be TxStatusPending state, but that's not what Figma wants + sendDetails: sendDetails, + sentTransaction: tx + )) proc suggestedFees*(self: Service, chainId: int): SuggestedFeesDto = try: @@ -347,8 +347,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 @@ -361,13 +388,25 @@ 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, routes: route, 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, @@ -384,7 +423,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) @@ -393,15 +432,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 = "", tokenIds: seq[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, + tokenIds, + 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/backend/community_tokens.nim b/src/backend/community_tokens.nim index 0134d08a04c..5d26dd4978c 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,20 @@ 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 storeDeployedCollectibles*(addressFrom: string, addressTo: string, chainId: int, txHash: string, deploymentParams: JsonNode): + RpcResponse[JsonNode] = + let payload = %* [addressFrom, addressTo, chainId, txHash, deploymentParams] + return core.callPrivateRPC("communitytokens_storeDeployedCollectibles", 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 storeDeployedAssets*(addressFrom: string, addressTo: string, chainId: int, txHash: string, deploymentParams: JsonNode): + RpcResponse[JsonNode] = + let payload = %* [addressFrom, addressTo, chainId, txHash, deploymentParams] + return core.callPrivateRPC("communitytokens_storeDeployedAssets", payload) proc removeCommunityToken*(chainId: int, address: string): RpcResponse[JsonNode] = let payload = %* [chainId, address] @@ -52,73 +54,26 @@ 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) -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) - -proc getOwnerTokenContractAddressFromHash*(chainId: int, transactionHash: string): RpcResponse[JsonNode] = - let payload = %*[chainId, transactionHash] - return core.callPrivateRPC("communitytokens_getOwnerTokenContractAddressFromHash", payload) - -proc createCommunityTokenDeploymentSignature*(chainId: int, addressFrom: string, signerAddress: string): RpcResponse[JsonNode] = - let payload = %*[chainId, addressFrom, signerAddress] - return core.callPrivateRPC("wakuext_createCommunityTokenDeploymentSignature", payload) - -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 storeDeployedOwnerToken*(addressFrom: string, chainId: int, txHash: string, ownerParams: JsonNode, masterParams: JsonNode): + RpcResponse[JsonNode] = + let payload = %* [addressFrom, chainId, txHash, ownerParams, masterParams] + return core.callPrivateRPC("communitytokens_storeDeployedOwnerToken", 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 registerOwnerTokenReceivedNotification*(communityId: string): RpcResponse[JsonNode] = let payload = %*[communityId] 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/SubscriptionBrokerCommunities.qml b/ui/StatusQ/src/StatusQ/Core/Utils/SubscriptionBrokerCommunities.qml new file mode 100644 index 00000000000..0b9ee5c805b --- /dev/null +++ b/ui/StatusQ/src/StatusQ/Core/Utils/SubscriptionBrokerCommunities.qml @@ -0,0 +1,200 @@ +import QtQuick 2.15 + +//This is a helper component that is used to batch requests and send them periodically +//It is used to reduce the number of requests sent to the server and notify different subscribers of the same request +//It is used by the Subscription component +QtObject { + id: root + + signal request(string subscriptionId, string topic) + + signal subscribed(string subscriptionId) + signal unsubscribed(string subscriptionId) + + 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) + } + function unsubscribe(subscription) { + d.unsubscribe(subscription) + } + + property bool active: true + + readonly property QtObject d: QtObject { + //Mapping subscriptionId to subscription object + //subscriptionId is a string and represents the id of the subscription + //E.g. "subscriptionId": {subscription: subscriptionObject, topic: "topic"} + //The purpose of this mapping is to keep track of the subscriptions and their topics + readonly property var managedSubscriptions: ({}) + + //Mapping topic to subscriptionIds and request data + //topic is a string and represents the topic of the subscription + //E.g. "topic": {nextRequestTimestamp: 0, requestInterval: 1000, subscriptions: ["subscriptionId1", "subscriptionId2"], response: null} + readonly property var topics: ({}) + property int topicsCount: 0 //helper property to track change events + + + + + + + + function subscribe(subscription) { + if(!(subscription instanceof Subscription)) + return + if(d.managedSubscriptions.hasOwnProperty(subscription.subscriptionId)) + return + + registerToManagedSubscriptions(subscription) + connectToSubscriptionEvents(subscription) + if(subscription.isReady && subscription.topic) + registerToTopic(subscription.topic, subscription.subscriptionId) + root.subscribed(subscription.subscriptionId) + } + + function unsubscribe(subscriptionId) { + if(!subscriptionId || !d.managedSubscriptions.hasOwnProperty(subscriptionId)) + return + + releaseManagedSubscription(subscriptionId) + root.unsubscribed(subscriptionId) + } + + function registerToManagedSubscriptions(subscriptionObject) { + d.managedSubscriptions[subscriptionObject.subscriptionId] = { + subscription: subscriptionObject, + topic: subscriptionObject.topic, + } + } + + function releaseManagedSubscription(subscriptionId) { + if(!subscriptionId || !d.managedSubscriptions.hasOwnProperty(subscriptionId)) return + + const subscriptionInfo = d.managedSubscriptions[subscriptionId] + + unregisterFromTopic(subscriptionInfo.topic, subscriptionId) + delete d.managedSubscriptions[subscriptionId] + } + + function connectToSubscriptionEvents(subscription) { + const subscriptionId = subscription.subscriptionId + const topic = subscription.topic + + const onTopicChangeHandler = () => { + if(!subscription.isReady || !d.managedSubscriptions.hasOwnProperty(subscriptionId)) return + + const newTopic = subscription.topic + const oldTopic = d.managedSubscriptions[subscriptionId].topic + + if(newTopic === oldTopic) return + + d.unregisterFromTopic(oldTopic, subscriptionId) + d.registerToTopic(newTopic, subscriptionId) + d.managedSubscriptions[subscriptionId].topic = newTopic + } + + const onReadyChangeHandler = () => { + if(!d.managedSubscriptions.hasOwnProperty(subscriptionId)) return + + if(subscription.isReady) { + d.registerToTopic(subscription.topic, subscription.subscriptionId) + } else { + const subscriptionTopic = d.managedSubscriptions[subscriptionId].topic + d.unregisterFromTopic(subscriptionTopic, subscriptionId) + } + } + + const onUnsubscribedHandler = (subscriptionId) => { + if(subscriptionId !== subscription.subscriptionId) + return + + subscription.Component.onDestruction.disconnect(onDestructionHandler) + subscription.isReadyChanged.disconnect(onReadyChangeHandler) + subscription.topicChanged.disconnect(onTopicChangeHandler) + } + + const onDestructionHandler = () => { + if(!d.managedSubscriptions.hasOwnProperty(subscriptionId)) + return + + root.unsubscribed.disconnect(onUnsubscribedHandler) //object is destroyed, no need to listen to the signal anymore + unsubscribe(subscriptionId) + } + + subscription.Component.onDestruction.connect(onDestructionHandler) + subscription.isReadyChanged.connect(onReadyChangeHandler) + subscription.topicChanged.connect(onTopicChangeHandler) + root.unsubscribed.connect(onUnsubscribedHandler) + } + + function registerToTopic(topic, subscriptionId) { + if(!d.topics.hasOwnProperty(topic)) { + d.topics[topic] = { + subscriptions: [], + response: null, + requestPending: false + } + d.topicsCount++ + } + + const index = d.topics[topic].subscriptions.indexOf(subscriptionId) + if(index !== -1) { + console.assert("Duplicate subscription: " + subscriptionId + " " + topic) + return + } + + const subscriptionsCount = d.topics[topic].subscriptions.push(subscriptionId) + if(subscriptionsCount === 1 && root.active) { + d.request(subscriptionId, topic) + } + d.managedSubscriptions[subscriptionId].subscription.response = d.topics[topic].response + } + + function unregisterFromTopic(topic, subscriptionId) { + if(!d.topics.hasOwnProperty(topic)) return + + const index = d.topics[topic].subscriptions.indexOf(subscriptionId) + if(index === -1) return + + d.topics[topic].subscriptions.splice(index, 1) + if(d.topics[topic].subscriptions.length === 0) { + delete d.topics[topic] + d.topicsCount-- + } + } + + + + + + function request(subscriptionId, topic) { + if(!d.topics.hasOwnProperty(topic)) return + + d.topics[topic].requestPending = true + + root.request(subscriptionId, topic) + } + + function onResponse(topic, responseObj) { + if(!d.topics.hasOwnProperty(topic)) return + + d.topics[topic].response = responseObj + d.topics[topic].subscriptions.forEach(function(subscriptionId) { + d.managedSubscriptions[subscriptionId].subscription.response = responseObj + }) + d.topics[topic].requestPending = false + } + } +} diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/qmldir b/ui/StatusQ/src/StatusQ/Core/Utils/qmldir index f98c58d52d1..f096f22653a 100644 --- a/ui/StatusQ/src/StatusQ/Core/Utils/qmldir +++ b/ui/StatusQ/src/StatusQ/Core/Utils/qmldir @@ -16,6 +16,7 @@ StackViewStates 0.1 StackViewStates.qml StatesStack 0.1 StatesStack.qml Subscription 0.1 Subscription.qml SubscriptionBroker 0.1 SubscriptionBroker.qml +SubscriptionBrokerCommunities 0.1 SubscriptionBrokerCommunities.qml XSS 1.0 xss.js singleton AmountsArithmetic 0.1 AmountsArithmetic.qml singleton Emoji 0.1 Emoji.qml diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc index 1379cbb10e8..56505ef5376 100644 --- a/ui/StatusQ/src/statusq.qrc +++ b/ui/StatusQ/src/statusq.qrc @@ -211,6 +211,7 @@ StatusQ/Core/Utils/StringUtils.qml StatusQ/Core/Utils/Subscription.qml StatusQ/Core/Utils/SubscriptionBroker.qml + StatusQ/Core/Utils/SubscriptionBrokerCommunities.qml StatusQ/Core/Utils/Utils.qml StatusQ/Core/Utils/big.min.mjs StatusQ/Core/Utils/emojiList.js 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/SetSignerFeesSubscriber.qml b/ui/app/AppLayouts/Communities/helpers/SetSignerFeesSubscriber.qml index 52c8b9e6750..f995b94aafa 100644 --- a/ui/app/AppLayouts/Communities/helpers/SetSignerFeesSubscriber.qml +++ b/ui/app/AppLayouts/Communities/helpers/SetSignerFeesSubscriber.qml @@ -8,6 +8,7 @@ import QtQuick 2.15 SingleFeeSubscriber { id: root + required property string communityId required property int chainId required property string contractAddress required property string accountAddress 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..51724a5e904 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 && @@ -101,13 +107,15 @@ QtObject { component SetSignerFeeSubscription: Subscription { required property SetSignerFeesSubscriber subscriber readonly property var requestArgs: ({ + communityId: subscriber.communityId, type: TransactionFeesBroker.FeeType.SetSigner, chainId: subscriber.chainId, contractAddress: subscriber.contractAddress, accountAddress: subscriber.accountAddress }) - isReady: !!subscriber.chainId && - !!subscriber.contractAddress && + isReady: !!subscriber.communityId && + !!subscriber.chainId && + !!subscriber.contractAddress && !!subscriber.accountAddress && subscriber.enabled @@ -121,83 +129,141 @@ QtObject { readonly property Component burnFeeSubscriptionComponent: BurnTokenFeeSubscription {} readonly property Component setSignerFeeSubscriptionComponent: SetSignerFeeSubscription {} - readonly property SubscriptionBroker feesBroker: SubscriptionBroker { + readonly property SubscriptionBrokerCommunities feesBroker: SubscriptionBrokerCommunities { 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) + computeSelfDestructFee(subscriptionId, args) break case TransactionFeesBroker.FeeType.Burn: - computeBurnFee(args, topic) + computeBurnFee(subscriptionId, args) break case TransactionFeesBroker.FeeType.SetSigner: - computeSetSignerFee(args, topic) + computeSetSignerFee(subscriptionId, args) break default: console.error("Unknown fee type: " + args.type) } } - 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) { - communityTokensStore.computeSelfDestructFee(args.walletsAndAmounts, args.tokenKey, args.accountAddress, topic) + function computeSelfDestructFee(subscriptionId, args) { + communityTokensStore.computeSelfDestructFee(subscriptionId, args.walletsAndAmounts, args.tokenKey, args.accountAddress) } - function computeBurnFee(args, topic) { + function computeBurnFee(subscriptionId, args) { console.assert(typeof args.amount === "string") - communityTokensStore.computeBurnFee(args.tokenKey, args.amount, args.accountAddress, topic) + communityTokensStore.computeBurnFee(subscriptionId, args.tokenKey, args.amount, args.accountAddress) } - function computeSetSignerFee(args, topic) { - communityTokensStore.computeSetSignerFee(args.chainId, args.contractAddress, args.accountAddress, topic) + function computeSetSignerFee(subscriptionId, args) { + communityTokensStore.computeSetSignerFee(subscriptionId, args.communityId, args.chainId, args.contractAddress, args.accountAddress) } } 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..68fd0081673 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 { @@ -660,8 +681,6 @@ StackView { accountAddress: tokenMasterActionPopup.selectedAccount tokenKey: view.token.key enabled: tokenMasterActionPopup.opened - Component.onCompleted: root.registerSelfDestructFeesSubscriber( - selfDestructFeesSubscriber) } SignTransactionsPopup { @@ -819,6 +838,18 @@ StackView { alertPopup.open() } + onCalculateFees: { + root.registerSelfDestructFeesSubscriber(remotelyDestructFeeSubscriber) + } + + onStopUpdatingFees: { + root.stopUpdatingFees() + } + + onClosed: { + root.stopUpdatingFees() + } + SelfDestructFeesSubscriber { id: remotelyDestructFeeSubscriber @@ -826,7 +857,6 @@ StackView { accountAddress: remotelyDestructPopup.selectedAccount tokenKey: view.token.key enabled: remotelyDestructPopup.tokenCount > 0 && accountAddress !== "" && (remotelyDestructPopup.opened || signTransactionPopup.opened) - Component.onCompleted: root.registerSelfDestructFeesSubscriber(remotelyDestructFeeSubscriber) } } @@ -863,6 +893,18 @@ StackView { signTransactionPopup.open() } + onCalculateFees: { + root.registerBurnTokenFeesSubscriber(burnTokensFeeSubscriber) + } + + onStopUpdatingFees: { + root.stopUpdatingFees() + } + + onClosed: { + root.stopUpdatingFees() + } + BurnTokenFeesSubscriber { id: burnTokensFeeSubscriber @@ -873,7 +915,6 @@ StackView { tokenKey: tokenViewPage.token.key accountAddress: burnTokensPopup.selectedAccountAddress enabled: burnTokensPopup.visible || signTransactionPopup.visible - Component.onCompleted: root.registerBurnTokenFeesSubscriber(burnTokensFeeSubscriber) } } diff --git a/ui/app/AppLayouts/Communities/popups/BurnTokensPopup.qml b/ui/app/AppLayouts/Communities/popups/BurnTokensPopup.qml index 20528dea441..3cdde29f584 100644 --- a/ui/app/AppLayouts/Communities/popups/BurnTokensPopup.qml +++ b/ui/app/AppLayouts/Communities/popups/BurnTokensPopup.qml @@ -43,6 +43,8 @@ StatusDialog { signal burnClicked(string burnAmount, string accountAddress) signal cancelClicked signal enableNetwork + signal calculateFees() + signal stopUpdatingFees() QtObject { id: d @@ -105,6 +107,7 @@ StatusDialog { } Item { + enabled: !showFees.checked Layout.bottomMargin: 12 Layout.leftMargin: -Theme.halfPadding Layout.fillWidth: true @@ -169,8 +172,23 @@ StatusDialog { Layout.fillWidth: true } + StatusSwitch { + id: showFees + enabled: d.isFormValid + text: qsTr("Show 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 placeholderText: qsTr("Choose number of tokens to burn to see gas fees") @@ -235,13 +253,14 @@ StatusDialog { normalColor: "transparent" onClicked: { + root.stopUpdatingFees() root.cancelClicked() close() } } StatusButton { - enabled: d.isFormValid && !d.isFeeError && !root.isFeeLoading + enabled: showFees.checked && !d.isFeeError && !root.isFeeLoading && root.feeText !== "" text: qsTr("Burn tokens") type: StatusBaseButton.Type.Danger diff --git a/ui/app/AppLayouts/Communities/popups/FinaliseOwnershipPopup.qml b/ui/app/AppLayouts/Communities/popups/FinaliseOwnershipPopup.qml index c7d3146aec1..39f67000db7 100644 --- a/ui/app/AppLayouts/Communities/popups/FinaliseOwnershipPopup.qml +++ b/ui/app/AppLayouts/Communities/popups/FinaliseOwnershipPopup.qml @@ -35,10 +35,14 @@ StatusDialog { // Account expected roles: address, name, color, emoji, walletType property var accounts + readonly property alias selectedAccountAddress: d.accountAddress + signal finaliseOwnershipClicked signal rejectClicked signal visitCommunityClicked signal openControlNodeDocClicked(string link) + signal calculateFees() + signal stopUpdatingFees() QtObject { id: d @@ -47,6 +51,7 @@ StatusDialog { readonly property int init: 0 readonly property int finalise: 1 + property bool feesActive: false property bool ackCheck: false // Fees related props: @@ -107,7 +112,7 @@ StatusDialog { PropertyChanges { target: acceptBtn text: qsTr("Make this device the control node and update smart contract") - enabled: d.ackCheck && !root.isFeeLoading && root.feeErrorText === "" + enabled: d.feesActive && !root.isFeeLoading && root.feeErrorText === "" onClicked: root.finaliseOwnershipClicked() } @@ -142,6 +147,7 @@ StatusDialog { text: qsTr("I don't want to be the owner") onClicked: { + root.stopUpdatingFees() root.rejectClicked() close() } @@ -292,8 +298,24 @@ StatusDialog { text: qsTr("This transaction updates the %1 Community smart contract, making you the %1 Community owner.").arg(root.communityName) } + StatusSwitch { + id: showFees + enabled: d.ackCheck + text: qsTr("Show fees (will be enabled once acknowledge confirmed)") + + onCheckedChanged: { + d.feesActive = checked + if(checked) { + root.calculateFees() + return + } + root.stopUpdatingFees() + } + } + FeesBox { id: feesBox + visible: showFees.checked Layout.fillWidth: true implicitWidth: 0 @@ -324,6 +346,7 @@ StatusDialog { } StatusCheckBox { + enabled: !showFees.checked Layout.topMargin: -Theme.halfPadding Layout.fillWidth: true diff --git a/ui/app/AppLayouts/Communities/popups/RemotelyDestructPopup.qml b/ui/app/AppLayouts/Communities/popups/RemotelyDestructPopup.qml index 6e902bb6418..2d51a831b92 100644 --- a/ui/app/AppLayouts/Communities/popups/RemotelyDestructPopup.qml +++ b/ui/app/AppLayouts/Communities/popups/RemotelyDestructPopup.qml @@ -40,6 +40,8 @@ StatusDialog { signal remotelyDestructClicked(var walletsAndAmounts, string accountAddress) signal enableNetwork + signal calculateFees() + signal stopUpdatingFees() QtObject { id: d @@ -101,9 +103,25 @@ StatusDialog { onSelfDestructRemoved: d.clearTokensToDestruct(walletAddress) } + StatusSwitch { + id: showFees + enabled: d.tokenCount > 0 + text: qsTr("Show 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.bottomMargin: networkWarningPanel.visible ? 0 : 16 Layout.leftMargin: 16 @@ -152,12 +170,13 @@ StatusDialog { StatusFlatButton { text: qsTr("Cancel") onClicked: { + root.stopUpdatingFees() root.close() } } StatusButton { - enabled: d.tokenCount > 0 text: qsTr("Remotely destruct %n token(s)", "", d.tokenCount) + enabled: showFees.checked type: StatusBaseButton.Type.Danger onClicked: { diff --git a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml index 751d09af20d..c4464fae93d 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,6 +372,7 @@ StatusSectionLayout { readonly property bool sectionEnabled: true sourceComponent: MintTokensSettingsPanel { + id: mintTokensSettingsPanel enabledChainIds: root.enabledChainIds readonly property CommunityTokensStore communityTokensStore: @@ -397,49 +402,75 @@ StatusSectionLayout { accounts: root.walletAccountsModel referenceAssetsBySymbolModel: root.tokensStore.assetsBySymbolModel - onRegisterDeployFeesSubscriber: d.feesBroker.registerDeployFeesSubscriber(feeSubscriber) + onStopUpdatingFees: { + communityTokensStore.stopUpdatesForSuggestedRoute() + } - onRegisterSelfDestructFeesSubscriber: d.feesBroker.registerSelfDestructFeesSubscriber(feeSubscriber) + onRegisterDeployFeesSubscriber: { + d.feesBroker.registerDeployFeesSubscriber(feeSubscriber) + } + + onRegisterSelfDestructFeesSubscriber: { + d.feesBroker.registerSelfDestructFeesSubscriber(feeSubscriber) + } - onRegisterBurnTokenFeesSubscriber: d.feesBroker.registerBurnFeesSubscriber(feeSubscriber) + onRegisterBurnTokenFeesSubscriber: { + d.feesBroker.registerBurnFeesSubscriber(feeSubscriber) + } - 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: - communityTokensStore.deployAsset(root.community.id, assetItem) + onMintAsset: { - onMintOwnerToken: - communityTokensStore.deployOwnerToken( - root.community.id, ownerToken, tMasterToken) + communityTokensStore.authenticateAndTransfer() + } - onRemotelyDestructCollectibles: - communityTokensStore.remoteSelfDestructCollectibles( - root.community.id, walletsAndAmounts, tokenKey, accountAddress) + onMintOwnerToken: { + communityTokensStore.authenticateAndTransfer() + } + + onRemotelyDestructCollectibles: { + communityTokensStore.authenticateAndTransfer() + } + + onRemotelyDestructAndBan: { - onRemotelyDestructAndBan: communityTokensStore.remotelyDestructAndBan( root.community.id, contactId, tokenKey, accountAddress) + } + + onRemotelyDestructAndKick: { - onRemotelyDestructAndKick: communityTokensStore.remotelyDestructAndKick( root.community.id, contactId, tokenKey, accountAddress) + } + + onBurnToken: { + + communityTokensStore.authenticateAndTransfer() + } - 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 +482,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) + } } } @@ -545,35 +581,31 @@ StatusSectionLayout { ] } - Connections { - target: root.rootStore.communityTokensStore - function onAirdropStateChanged(communityId, tokenName, chainName, status, url) { - if (root.community.id !== communityId) { - return - } - if (status == Constants.ContractTransactionStatus.InProgress) { - airdropsSettingsPanel.navigateBack() - } - } - } membersModel: root.joinedMembers enabledChainIds: root.enabledChainIds 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: { + d.feesBroker.registerAirdropFeesSubscriber(feeSubscriber) + } + } } } @@ -706,159 +738,6 @@ StatusSectionLayout { } - Connections { - target: rootStore.communityTokensStore - - function onRemoteDestructStateChanged(communityId, tokenName, 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("Remotely destroying tokens...") - loading = true - break - case Constants.ContractTransactionStatus.Completed: - title = qsTr("%1 tokens destroyed").arg(tokenName) - type = Constants.ephemeralNotificationType.success - break - case Constants.ContractTransactionStatus.Failed: - title = qsTr("%1 tokens destruction failed").arg(tokenName) - break - default: - console.warn("Unknown destruction state: "+status) - return - } - - Global.displayToastMessage(title, qsTr("View on etherscan"), "", - 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) - return - - let title = "" - let loading = false - let type = Constants.ephemeralNotificationType.normal - - switch (status) { - case Constants.ContractTransactionStatus.InProgress: - title = qsTr("%1 being burned...").arg(tokenName) - loading = true - break - case Constants.ContractTransactionStatus.Completed: - title = qsTr("%1 burning is complete").arg(tokenName) - type = Constants.ephemeralNotificationType.success - break - case Constants.ContractTransactionStatus.Failed: - title = qsTr("%1 burning is failed").arg(tokenName) - break - default: - console.warn("Unknown burning state: "+status) - return - } - - Global.displayToastMessage(title, qsTr("View on etherscan"), "", - 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 { target: root.chatCommunitySectionModule 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..3c76036d89b 100644 --- a/ui/app/AppLayouts/Communities/views/MintedTokensView.qml +++ b/ui/app/AppLayouts/Communities/views/MintedTokensView.qml @@ -193,9 +193,11 @@ StatusScrollView { color: Theme.palette.baseColor1 } ] - onClicked: root.itemClicked(model.contractUniqueKey, + onClicked: { + root.itemClicked(model.contractUniqueKey, model.chainId, model.chainName, model.accountName, model.address) + } } } @@ -256,9 +258,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 df1013b2e65..6957e9867ac 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -80,7 +80,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 @@ -279,6 +281,23 @@ Item { username: string, publicKey: string, packId: string, + communityId: string, + communityName: string, + communityInvolvedTokens: int, + communityTotalAmount: string, + communityAmount1: string, + communityAmountInfinite1: bool, + communityAssetName1: string, + communityAssetDecimals1: int, + communityAmount2: string, + communityAmountInfinite2: bool, + communityAssetName2: string, + communityAssetDecimals2: int, + communityInvolvedAddress: string, + communityNubmerOfInvolvedAddresses: int, + communityOwnerTokenName: string, + communityMasterTokenName: string, + communityDeployedTokenName: string, status: string, error: string) { @@ -302,6 +321,9 @@ Item { let ensName = d.ensName(username) let stickersPackName = qsTr("unknown") + let sentCommunityAmount1 = "" + let sentCommunityAmount2 = "" + if (!!txHash) { toastLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(fromChainId)).arg(txHash) } @@ -344,6 +366,16 @@ Item { } } + if (!!communityAmount1) { + let bigIntCommunityAmount1 = SQUtils.AmountsArithmetic.fromString(communityAmount1) + sentCommunityAmount1 = SQUtils.AmountsArithmetic.toNumber(bigIntCommunityAmount1, communityAssetDecimals1) + } + + if (!!communityAmount2) { + let bigIntCommunityAmount2 = SQUtils.AmountsArithmetic.fromString(communityAmount2) + sentCommunityAmount2 = SQUtils.AmountsArithmetic.toNumber(bigIntCommunityAmount2, communityAssetDecimals2) + } + switch(status) { case Constants.txStatus.sending: { toastTitle = qsTr("Sending %1 from %2 to %3") @@ -392,6 +424,60 @@ Item { } break } + case Constants.SendType.CommunityDeployAssets: { + if (communityAmountInfinite1) { + toastTitle = qsTr("Minting infinite %1 tokens for %2 using %3").arg(communityDeployedTokenName).arg(communityName).arg(sender) + } else { + toastTitle = qsTr("Minting %1 %2 tokens for %3 using %4").arg(sentCommunityAmount1).arg(communityDeployedTokenName).arg(communityName).arg(sender) + } + break + } + case Constants.SendType.CommunityDeployCollectibles: { + if (communityAmountInfinite1) { + toastTitle = qsTr("Minting infinite %1 tokens for %2 using %3").arg(communityDeployedTokenName).arg(communityName).arg(sender) + } else { + toastTitle = qsTr("Minting %1 %2 tokens for %3 using %4").arg(sentCommunityAmount1).arg(communityDeployedTokenName).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: { + if (!sentCommunityAmount2) { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Airdropping %1x %2 to %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Airdropping %1x %2 to %3 addresses using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + } else if(communityInvolvedTokens === 2) { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Airdropping %1x %2 and %3x %4 to %5 using %6").arg(sentCommunityAmount1).arg(communityAssetName1).arg(sentCommunityAmount2).arg(communityAssetName2).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Airdropping %1x %2 and %3x %4 to %5 addresses using %6").arg(sentCommunityAmount1).arg(communityAssetName1).arg(sentCommunityAmount2).arg(communityAssetName2).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + } else { + toastTitle = qsTr("Airdropping %1 tokens to %2 using %3").arg(communityInvolvedTokens).arg(communityInvolvedAddress).arg(sender) + } + break + } + case Constants.SendType.CommunityRemoteBurn: { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Destroying %1x %2 at %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Destroying %1x %2 at %3 addresses using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + break + } + case Constants.SendType.CommunityBurn: { + toastTitle = qsTr("Burning %1x %2 for %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunitySetSignerPubKey: { + toastTitle = qsTr("Finalizing ownership for %1 using %2").arg(communityName).arg(sender) + break + } case Constants.SendType.Approve: { console.warn("tx type approve not yet identified as a stand alone path") break @@ -456,6 +542,60 @@ Item { } break } + case Constants.SendType.CommunityDeployAssets: { + if (communityAmountInfinite1){ + toastTitle = qsTr("Minted infinite %1 tokens for %2 using %3").arg(communityDeployedTokenName).arg(communityName).arg(sender) + } else { + toastTitle = qsTr("Minted %1 %2 tokens for %3 using %4").arg(sentCommunityAmount1).arg(communityDeployedTokenName).arg(communityName).arg(sender) + } + break + } + case Constants.SendType.CommunityDeployCollectibles: { + if (communityAmountInfinite1){ + toastTitle = qsTr("Minted infinite %1 tokens for %2 using %3").arg(communityDeployedTokenName).arg(communityName).arg(sender) + } else { + toastTitle = qsTr("Minted %1 %2 tokens for %3 using %4").arg(sentCommunityAmount1).arg(communityDeployedTokenName).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: { + if (!sentCommunityAmount2) { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Airdropped %1x %2 to %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Airdropped %1x %2 to %3 addresses using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + } else if(communityInvolvedTokens === 2) { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Airdropped %1x %2 and %3x %4 to %5 using %6").arg(sentCommunityAmount1).arg(communityAssetName1).arg(sentCommunityAmount2).arg(communityAssetName2).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Airdropped %1x %2 and %3x %4 to %5 addresses using %6").arg(sentCommunityAmount1).arg(communityAssetName1).arg(sentCommunityAmount2).arg(communityAssetName2).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + } else { + toastTitle = qsTr("Airdropped %1 tokens to %2 using %3").arg(communityInvolvedTokens).arg(communityInvolvedAddress).arg(sender) + } + break + } + case Constants.SendType.CommunityRemoteBurn: { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Destroyed %1x %2 at %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Destroyed %1x %2 at %3 addresses using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + break + } + case Constants.SendType.CommunityBurn: { + toastTitle = qsTr("Burned %1x %2 for %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunitySetSignerPubKey: { + toastTitle = qsTr("Finalized ownership for %1 using %2").arg(communityName).arg(sender) + break + } case Constants.SendType.Approve: { console.warn("tx type approve not yet identified as a stand alone path") break @@ -514,6 +654,60 @@ Item { } break } + case Constants.SendType.CommunityDeployAssets: { + if (communityAmountInfinite1){ + toastTitle = qsTr("Mint failed: infinite %1 tokens for %2 using %3").arg(communityDeployedTokenName).arg(communityName).arg(sender) + } else { + toastTitle = qsTr("Mint failed: %1 %2 tokens for %3 using %4").arg(sentCommunityAmount1).arg(communityDeployedTokenName).arg(communityName).arg(sender) + } + break + } + case Constants.SendType.CommunityDeployCollectibles: { + if (communityAmountInfinite1){ + toastTitle = qsTr("Mint failed: infinite %1 tokens for %2 using %3").arg(communityDeployedTokenName).arg(communityName).arg(sender) + } else { + toastTitle = qsTr("Mint failed: %1 %2 tokens for %3 using %4").arg(sentCommunityAmount1).arg(communityDeployedTokenName).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: { + if (!sentCommunityAmount2) { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Airdrop failed: %1x %2 to %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Airdrop failed: %1x %2 to %3 addresses using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + } else if(communityInvolvedTokens === 2) { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Airdrop failed: %1x %2 and %3x %4 to %5 using %6").arg(sentCommunityAmount1).arg(communityAssetName1).arg(sentCommunityAmount2).arg(communityAssetName2).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Airdrop failed: %1x %2 and %3x %4 to %5 addresses using %6").arg(sentCommunityAmount1).arg(communityAssetName1).arg(sentCommunityAmount2).arg(communityAssetName2).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + } else { + toastTitle = qsTr("Airdrop failed: %1 tokens to %2 using %3").arg(communityInvolvedTokens).arg(communityInvolvedAddress).arg(sender) + } + break + } + case Constants.SendType.CommunityRemoteBurn: { + if (communityNubmerOfInvolvedAddresses === 1 && !!communityInvolvedAddress) { + toastTitle = qsTr("Destruction failed: %1x %2 at %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityInvolvedAddress).arg(sender) + } else { + toastTitle = qsTr("Destruction failed: %1x %2 at %3 addresses using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityNubmerOfInvolvedAddresses).arg(sender) + } + break + } + case Constants.SendType.CommunityBurn: { + toastTitle = qsTr("Burn failed: %1x %2 for %3 using %4").arg(sentCommunityAmount1).arg(communityAssetName1).arg(communityName).arg(sender) + break + } + case Constants.SendType.CommunitySetSignerPubKey: { + toastTitle = qsTr("Finalize ownership failed: %1 using %2").arg(communityName).arg(sender) + break + } case Constants.SendType.Approve: { console.warn("tx type approve not yet identified as a stand alone path") break @@ -525,8 +719,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 54d247176be..a099c1c8f5d 100644 --- a/ui/app/mainui/Popups.qml +++ b/ui/app/mainui/Popups.qml @@ -1101,6 +1101,11 @@ QtObject { readonly property var ownerTokenDetails: root.communityTokensStore.ownerTokenDetails readonly property var communityData : root.communitiesStore.getCommunityDetailsAsJson(communityId) + readonly property TransactionFeesBroker feesBroker: TransactionFeesBroker { + communityTokensStore: root.communityTokensStore + active: finalisePopup.contentItem.Window.window.active + } + Component.onCompleted: root.communityTokensStore.asyncGetOwnerTokenDetails(communityId) communityName: communityData.name @@ -1124,19 +1129,25 @@ QtObject { onVisitCommunityClicked: communitiesStore.navigateToCommunity(finalisePopup.communityId) onOpenControlNodeDocClicked: Global.openLink(link) - SetSignerFeesSubscriber { - id: feeSubscriber + onCalculateFees: { + feesBroker.registerSetSignerFeesSubscriber(feeSubscriber) + } - readonly property TransactionFeesBroker feesBroker: TransactionFeesBroker { - communityTokensStore: root.communityTokensStore - active: finalisePopup.contentItem.Window.window.active - } + onStopUpdatingFees: { + communityTokensStore.stopUpdatesForSuggestedRoute() + } + + onClosed: { + communityTokensStore.stopUpdatesForSuggestedRoute() + } + SetSignerFeesSubscriber { + id: feeSubscriber + communityId: finalisePopup.communityId chainId: finalisePopup.ownerTokenDetails.chainId contractAddress: finalisePopup.ownerTokenDetails.contractAddress - accountAddress: finalisePopup.ownerTokenDetails.accountAddress + accountAddress: finalisePopup.selectedAccountAddress enabled: finalisePopup.visible || signPopup.visible - Component.onCompleted: feesBroker.registerSetSignerFeesSubscriber(feeSubscriber) } SignTransactionsPopup { @@ -1155,7 +1166,7 @@ QtObject { onSignTransactionClicked: { finalisePopup.close() - root.communityTokensStore.updateSmartContract(finalisePopup.communityId, finalisePopup.ownerTokenDetails.chainId, finalisePopup.ownerTokenDetails.contractAddress, finalisePopup.ownerTokenDetails.accountAddress) + root.communityTokensStore.authenticateAndTransfer() } } diff --git a/ui/app/mainui/ToastsManager.qml b/ui/app/mainui/ToastsManager.qml index 1badca23254..2b495b93313 100644 --- a/ui/app/mainui/ToastsManager.qml +++ b/ui/app/mainui/ToastsManager.qml @@ -61,38 +61,6 @@ QtObject { communityId) } - function onSetSignerStateChanged(communityId, communityName, status, url) { - if (status === Constants.ContractTransactionStatus.Completed) { - Global.displayToastMessage(qsTr("%1 smart contract amended").arg(communityName), - root.viewOptimismExplorerText, - root.checkmarkCircleAssetName, - false, - Constants.ephemeralNotificationType.success, - url) - Global.displayToastWithActionMessage(qsTr("Your device is now the control node for %1. You now have full Community admin rights.").arg(communityName), - qsTr("%1 Community admin").arg(communityName), - root.checkmarkCircleAssetName, - "", - false, - Constants.ephemeralNotificationType.success, - ToastsManager.ActionType.NavigateToCommunityAdmin, - communityId) - } else if (status === Constants.ContractTransactionStatus.Failed) { - Global.displayToastMessage(qsTr("%1 smart contract update failed").arg(communityName), - root.viewOptimismExplorerText, - root.warningAssetName, - false, - Constants.ephemeralNotificationType.danger, - url) - } else if (status === Constants.ContractTransactionStatus.InProgress) { - Global.displayToastMessage(qsTr("Updating %1 smart contract").arg(communityName), - root.viewOptimismExplorerText, - "", - true, - Constants.ephemeralNotificationType.normal, - url) - } - } function onCommunityOwnershipDeclined(communityName) { Global.displayToastWithActionMessage(qsTr("You declined ownership of %1.").arg(communityName), @@ -105,22 +73,6 @@ QtObject { "") } - // Ownership Sender: - function onSendOwnerTokenStateChanged(tokenName, status, url) { - if (status === Constants.ContractTransactionStatus.InProgress) { - Global.displayToastMessage(qsTr("Sending %1 token").arg(tokenName), - root.viewOptimismExplorerText, - "", - true, - Constants.ephemeralNotificationType.normal, url) - } else if (status === Constants.ContractTransactionStatus.Completed) { - Global.displayToastMessage(qsTr("%1 token sent").arg(tokenName), - root.viewOptimismExplorerText, - root.checkmarkCircleAssetName, - false, - Constants.ephemeralNotificationType.success, url) - } - } function onOwnershipLost(communityId, communityName) { Global.displayToastMessage(qsTr("Your device is no longer the control node for %1. diff --git a/ui/imports/shared/stores/CommunityTokensStore.qml b/ui/imports/shared/stores/CommunityTokensStore.qml index eac1a210e22..2573639d28e 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,22 +23,8 @@ 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) - signal sendOwnerTokenStateChanged(string tokenName, int status, string url) signal ownerTokenReceived(string communityId, string communityName) signal communityTokenReceived(string name, string symbol, string image, string communityId, string communityName, @@ -45,47 +33,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]) @@ -96,9 +100,6 @@ QtObject { communityTokensModuleInst.refreshCommunityToken(parts[0], parts[1]) } - function updateSmartContract(communityId, chainId, contractAddress, accountAddress) { - communityTokensModuleInst.setSigner(communityId, chainId, contractAddress, accountAddress) - } function ownershipDeclined(communityId, communityName) { communityTokensModuleInst.declineOwnership(communityId) @@ -108,50 +109,6 @@ 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) - } - - function onSetSignerFeeUpdated(ethCurrency, fiatCurrency, errorCode, responseId) { - 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) - } - - function onAirdropStateChanged(communityId, tokenName, chainName, status, url) { - root.airdropStateChanged(communityId, tokenName, chainName, status, url) - } - - function onBurnStateChanged(communityId, tokenName, status, url) { - root.burnStateChanged(communityId, tokenName, status, url) - } - function onOwnerTokenReceived(communityId, communityName, chainId, communityAddress) { root.ownerTokenReceived(communityId, communityName) } @@ -160,37 +117,23 @@ QtObject { root.communityTokenReceived(name, symbol, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, walletAccountName, walletAccountName, walletAddress) } - function onSetSignerStateChanged(communityId, communityName, status, url) { - root.setSignerStateChanged(communityId, communityName, status, url) - } - function onOwnershipNodeLost(communityId, communityName) { root.ownershipLost(communityId, communityName) } + } - function onSendOwnerTokenStateChanged(tokenName, status, url) { - root.sendOwnerTokenStateChanged(tokenName, status, url) - } + function stopUpdatesForSuggestedRoute() { + communityTokensModuleInst.stopUpdatesForSuggestedRoute() } // Burn: - function computeBurnFee(tokenKey, amount, accountAddress, requestId) { + function computeBurnFee(subscriptionId, tokenKey, amount, accountAddress) { 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) + communityTokensModuleInst.computeBurnFee(subscriptionId, tokenKey, amount, accountAddress) } - 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) + function computeSetSignerFee(subscriptionId, communityId, chainId, contractAddress, accountAddress) { + communityTokensModuleInst.computeSetSignerFee(subscriptionId, communityId, chainId, contractAddress, accountAddress) } /** @@ -202,13 +145,10 @@ QtObject { * } * ] */ - function computeSelfDestructFee(walletsAndAmounts, tokenKey, accountAddress, requestId) { - communityTokensModuleInst.computeSelfDestructFee(JSON.stringify(walletsAndAmounts), tokenKey, accountAddress, requestId) + function computeSelfDestructFee(subscriptionId, walletsAndAmounts, tokenKey, accountAddress) { + communityTokensModuleInst.computeSelfDestructFee(subscriptionId, JSON.stringify(walletsAndAmounts), tokenKey, accountAddress) } - function remoteSelfDestructCollectibles(communityId, walletsAndAmounts, tokenKey, accountAddress) { - communityTokensModuleInst.selfDestructCollectibles(communityId, JSON.stringify(walletsAndAmounts), tokenKey, accountAddress) - } function remotelyDestructAndBan(communityId, contactId, tokenKey, accountAddress, deleteMessages) { console.warn("remotelyDestructAndBan, not implemented yet!") @@ -218,15 +158,7 @@ QtObject { console.warn("remotelyDestructAndKick, not implemented yet!") } - function burnToken(communityId, tokenKey, burnAmount, accountAddress) { - console.assert(typeof burnAmount === "string") - 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 826225c609b..c2839ecdbb9 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -1014,6 +1014,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 e4d5622a345..90a72ac4faf 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit e4d5622a345535ddb207163eab2d2d95a37b48a8 +Subproject commit 90a72ac4fafcadef28b592ccdc6a9a9511503062