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