From dff62efbe1b7c9a6d2d14a34f5f6499c4aad0d17 Mon Sep 17 00:00:00 2001 From: lvshaoping007 <1244849816@qq.com> Date: Mon, 17 Jan 2022 11:29:50 +0800 Subject: [PATCH] change deposit tx flow --- src/background/api/index.js | 26 +++-- src/background/api/request.js | 27 +++-- src/background/api/txHelper.js | 159 +++++++++++++++++++++++++-- src/background/service/APIService.js | 126 +++++++++++++-------- src/constant/storageKey.js | 3 + src/constant/types.js | 7 ++ src/i18n/en.json | 3 +- src/i18n/zh_CN.json | 4 +- src/popup/pages/Paratime/index.js | 89 ++++++++++++++- src/popup/pages/Paratime/index.scss | 9 ++ src/popup/pages/Record/index.js | 10 +- src/popup/pages/Send/index.js | 8 +- src/popup/pages/Wallet/index.js | 5 +- src/utils/commonMsg.js | 16 +++ src/utils/utils.js | 62 ++++++++++- 15 files changed, 455 insertions(+), 99 deletions(-) diff --git a/src/background/api/index.js b/src/background/api/index.js index 3a52a5d6..c63ac97b 100644 --- a/src/background/api/index.js +++ b/src/background/api/index.js @@ -81,12 +81,19 @@ export async function getNodeStakeList() { /** * get tx hash - * @param {*} txHash + * @param {*} url + * @param {*} hash * @returns */ -export async function getSubmitStatus(txhash) { - let url = "/chain/transaction/" + txhash - let txStatus = await commonFetch(url).catch(() => { }) +export async function getSubmitStatus(hash,url){ + let fetchUrl + if(url){ + fetchUrl = url + "/chain/transaction/" + hash + }else{ + fetchUrl = "/chain/transaction/" + hash + } + + let txStatus = await commonFetch(fetchUrl).catch(() => { }) if (txStatus && txStatus.code === 0) { return txStatus.data } else { @@ -132,11 +139,16 @@ export async function getRpcRuntimeList(){ /** * get runtime tx status - * @param {*} txHash - * @returns + * @param {*} txhash + * @param {*} runtimeId + * @param {*} baseFetchUrl + * @returns */ - export async function getRuntimeTxDetail(txhash,runtimeId) { + export async function getRuntimeTxDetail(txhash,runtimeId,baseFetchUrl) { let url = `/runtime/transaction/info?id=${runtimeId}&hash=${txhash}` + if(baseFetchUrl){ + url = baseFetchUrl+ url + } let txDetail = await commonFetch(url).catch(() => { }) if (txDetail && txDetail.code === 0) { return txDetail.data diff --git a/src/background/api/request.js b/src/background/api/request.js index 9dcec83d..d6aff34d 100644 --- a/src/background/api/request.js +++ b/src/background/api/request.js @@ -3,12 +3,13 @@ import axios from "axios"; import { NETWORK_CONFIG } from "../../constant/storageKey"; import "./axios"; import { getLocal } from "../storage/localStorage"; +import { isContainBaseUrl } from "../../utils/utils" export function getNowUrl() { - let localNetConfig = getLocal(NETWORK_CONFIG) + let localNetConfigStr = getLocal(NETWORK_CONFIG) let url = {} - if (localNetConfig) { - localNetConfig = JSON.parse(localNetConfig) + if (localNetConfigStr) { + let localNetConfig = JSON.parse(localNetConfigStr) let currentList = localNetConfig.currentNetList currentList = currentList.filter((item, index) => { return item.isSelect @@ -19,8 +20,13 @@ export function getNowUrl() { } export async function commonFetch(url) { - let real = getNowUrl() - let fetchUrl = real.url + url + let fetchUrl + if(isContainBaseUrl(url)){ + fetchUrl = url + }else{ + let real = getNowUrl() + fetchUrl = real.url + url + } return new Promise((resolve, reject) => { axios.get(fetchUrl).then((response) => { resolve(response.data) @@ -30,9 +36,14 @@ export async function commonFetch(url) { }) } -export function getOasisClient(){ - let real = getNowUrl() - let fetchUrl = real.grpc +export function getOasisClient(config){ + let fetchUrl + if(config && config.grpc){ + fetchUrl = config.grpc + }else{ + let real = getNowUrl() + fetchUrl = real.grpc + } const oasisClient = new oasis.client.NodeInternal(fetchUrl) return oasisClient } diff --git a/src/background/api/txHelper.js b/src/background/api/txHelper.js index 7451e843..e96b820b 100644 --- a/src/background/api/txHelper.js +++ b/src/background/api/txHelper.js @@ -7,6 +7,10 @@ import { TRANSACTION_TYPE } from '../../constant/walletType'; import { amountDecimals, getEvmBech32Address, getRuntimeConfig, hex2uint, isEvmAddress, toNonExponential } from '../../utils/utils'; import { getOasisClient } from './request'; import * as oasisRT from '@oasisprotocol/client-rt'; +import { getLocal, saveLocal } from '../storage/localStorage'; +import { APPROVE_AND_DEPOSIT_TRANSACTION } from "../../constant/storageKey" +import extension from 'extensionizer'; +import { APPROVE_TRANSACTION_UPDATE, FROM_BACK_TO_POPUP, FROM_BACK_TO_RECORD, NET_CONFIG_TYPE_MAIN } from "../../constant/types" const RETRY_TIME = 4 const RETRY_DELAY = 1000 @@ -78,21 +82,21 @@ export async function getTxFee(tw, publicKey, retryTime) { * @param {*} retryTime * @returns */ -export async function getChainContext(retryTime) { +export async function getChainContext(retryTime,currentNetConfig) { if (process.env.NODE_ENV === 'test') { return TEST_NET_CONTEXT } else { return new Promise(async (resolve, reject) => { retryTime = retryTime - 1 try { - const oasisClient = getOasisClient() + const oasisClient = getOasisClient(currentNetConfig) let chainContext = await oasisClient.consensusGetChainContext() resolve(chainContext) } catch (error) { if (retryTime > 0) { setTimeout(async () => { try { - resolve(await getChainContext(retryTime)) + resolve(await getChainContext(retryTime,currentNetConfig)) } catch (error) { reject(error) } @@ -111,21 +115,21 @@ export async function getChainContext(retryTime) { * @param {*} retryTime * @returns */ -export async function submitTx(tw, retryTime) { +export async function submitTx(tw, retryTime,currentNetConfig) { if (process.env.NODE_ENV === 'test') { return } else { return new Promise(async (resolve, reject) => { retryTime = retryTime - 1 try { - const oasisClient = getOasisClient() + const oasisClient = getOasisClient(currentNetConfig) let signSubmit = await tw.submit(oasisClient) resolve(signSubmit) } catch (error) { if (retryTime > 0) { setTimeout(async () => { try { - resolve(await submitTx(tw, retryTime)) + resolve(await submitTx(tw, retryTime,currentNetConfig)) } catch (err) { reject(err) } @@ -248,18 +252,18 @@ export async function submitTxBody(params, tw) { * @param {*} retryTime * @returns */ -function getRuntimeNonce(accountsWrapper,address,retryTime){ + function getRuntimeNonce(accountsWrapper,address,retryTime,currentNetConfig){ return new Promise(async (resolve, reject) => { retryTime = retryTime - 1 try { - const oasisClient = getOasisClient() + const oasisClient = getOasisClient(currentNetConfig) let nonceResult = await accountsWrapper.queryNonce().setArgs({ address: address }).query(oasisClient); resolve(nonceResult) } catch (error) { if (retryTime > 0) { setTimeout(async () => { try { - resolve(await getRuntimeNonce(accountsWrapper,address,retryTime)) + resolve(await getRuntimeNonce(accountsWrapper,address,retryTime,currentNetConfig)) } catch (error) { reject(error) } @@ -278,12 +282,12 @@ function getRuntimeNonce(accountsWrapper,address,retryTime){ * @param {*} wrapper * @returns */ -export async function buildParatimeTxBody(params, wrapper) { + export async function buildParatimeTxBody(params, wrapper,currentNetConfig) { const CONSENSUS_RT_ID = oasis.misc.fromHex(params.runtimeId) const accountsWrapper = new oasisRT.accounts.Wrapper(CONSENSUS_RT_ID); let bech32Address = await oasis.staking.addressFromBech32(params.fromAddress) - const nonce = await getRuntimeNonce(accountsWrapper,bech32Address,RETRY_TIME) + const nonce = await getRuntimeNonce(accountsWrapper,bech32Address,RETRY_TIME,currentNetConfig) let decimal let runtimeConfig = getRuntimeConfig(params.runtimeId) @@ -309,7 +313,7 @@ export async function buildParatimeTxBody(params, wrapper) { // Use default if feeGas is "" or 0 (0 is illegal in send page) let feeGas = params.feeGas||50000 feeGas = BigInt(feeGas) - let consensusChainContext = await getChainContext(RETRY_TIME) + let consensusChainContext = await getChainContext(RETRY_TIME,currentNetConfig) let txWrapper let targetAddress = "" @@ -337,5 +341,136 @@ export async function buildParatimeTxBody(params, wrapper) { txWrapper.setFeeAmount(feeAmount) .setFeeGas(feeGas) .setFeeConsensusMessages(1) + return {txWrapper,nonce,consensusChainContext} +} + +/** + * get local depositAnd approve pending tx by address + * @param {*} address + * @param {*} netType + * @returns + */ +export function getLocalApproveAndDepositTransactionCacheByAddress(address,netType) { + let currentNetType = netType||NET_CONFIG_TYPE_MAIN + let addressTxList = [] + let localTxListConfig = getLocal(APPROVE_AND_DEPOSIT_TRANSACTION) + if (localTxListConfig) { + let localTxList = JSON.parse(localTxListConfig) + let currentNetTxList = localTxList[currentNetType] || {} + addressTxList = currentNetTxList[address] || [] + } + return addressTxList +} + +/** + * save approve tx transaction + * @param {*} address + * @param {*} txHash + * @param {*} netType + */ +export function setLocalApproveTransactionCache(address, txHash, netType) { + let currentNetType = netType||NET_CONFIG_TYPE_MAIN + + let addressTxList = [] + let localTxListConfig = getLocal(APPROVE_AND_DEPOSIT_TRANSACTION) + if (localTxListConfig) { + let localTxList = JSON.parse(localTxListConfig) + let currentNetTxList = localTxList[currentNetType] + if(!currentNetTxList){ + localTxList[currentNetType] = {} + currentNetTxList = {} + } + addressTxList = currentNetTxList[address] || [] + addressTxList.push(txHash) + + localTxList[currentNetType][address] = addressTxList + saveLocal(APPROVE_AND_DEPOSIT_TRANSACTION, JSON.stringify(localTxList)) + } else { + let newConfigTxList = { + [currentNetType]: {} + } + newConfigTxList[currentNetType][address] = [] + newConfigTxList[currentNetType][address].push(txHash) + + saveLocal(APPROVE_AND_DEPOSIT_TRANSACTION, JSON.stringify(newConfigTxList)) + } + notifyApproveTransactionUpdate() +} + +/** + * add deposit transaction hash and runtime to approve hash + * @param {*} address + * @param {*} approveHash + * @param {*} runtimeId + * @param {*} txHash + * @param {*} netType + */ +export function setLocalDepositTransactionCache(address, approveHash,runtimeId,txHash, netType) { + let currentNetType = netType||NET_CONFIG_TYPE_MAIN + + let addressTxList = [] + let localTxListConfig = getLocal(APPROVE_AND_DEPOSIT_TRANSACTION) + if (localTxListConfig) { + let localTxList = JSON.parse(localTxListConfig) + let currentNetTxList = localTxList[currentNetType] + + if(currentNetTxList){ + addressTxList = currentNetTxList[address] + if(addressTxList){ + for (let index = 0; index < addressTxList.length; index++) { + const txData = addressTxList[index]; + if(txData === approveHash){ + localTxList[currentNetType][address][index] = approveHash+"+"+runtimeId+"+"+txHash + saveLocal(APPROVE_AND_DEPOSIT_TRANSACTION, JSON.stringify(localTxList)) + break + } + } + } + + } + } +} + +/** + * remove txHash when success or throw error + * @param {*} data + * @param {*} netType + * @returns + */ +export function removeLocalApproveAndDepositTransactionCacheByHash(data,netType) { + if(!Array.isArray(data)){ + return + } + let deleteValue = data + + let currentNetType = netType||NET_CONFIG_TYPE_MAIN + + let localTxListConfig = getLocal(APPROVE_AND_DEPOSIT_TRANSACTION) + if (localTxListConfig) { + let localTxList = JSON.parse(localTxListConfig) + let currentNetTxList = localTxList[currentNetType] || {} + + let addressList = Object.keys(currentNetTxList) + for (let index = 0; index < addressList.length; index++) { + const address = addressList[index]; + let newList = currentNetTxList[address].filter((tx) => { + return deleteValue.indexOf(tx) === -1 + }) + currentNetTxList[address] = newList + } + + localTxList[currentNetType] = currentNetTxList + saveLocal(APPROVE_AND_DEPOSIT_TRANSACTION, JSON.stringify(localTxList)) + + notifyApproveTransactionUpdate() + } +} + + +function notifyApproveTransactionUpdate() { + extension.runtime.sendMessage({ + type: FROM_BACK_TO_POPUP, + action: APPROVE_TRANSACTION_UPDATE, + }); } \ No newline at end of file diff --git a/src/background/service/APIService.js b/src/background/service/APIService.js index 37775cb7..306a91a6 100644 --- a/src/background/service/APIService.js +++ b/src/background/service/APIService.js @@ -11,10 +11,11 @@ import { RUNTIME_ACCOUNT_TYPE } from '../../constant/paratimeConfig'; import { FROM_BACK_TO_RECORD, SET_LOCK, TX_SUCCESS } from '../../constant/types'; import { ACCOUNT_TYPE, TRANSACTION_RUNTIME_TYPE, TRANSACTION_TYPE } from "../../constant/walletType"; import { getLanguage } from '../../i18n'; -import { openTab } from "../../utils/commonMsg"; -import { amountDecimals, getEvmBech32Address, getExplorerUrl, getRuntimeConfig, hex2uint, publicKeyToAddress, toNonExponential, trimSpace, uint2hex } from '../../utils/utils'; +import { openTab, showErrorNotification } from "../../utils/commonMsg"; +import { amountDecimals, getEvmBech32Address, getCurrentNetConfig, getRuntimeConfig, hex2uint, publicKeyToAddress, toNonExponential, trimSpace, uint2hex, getNetTypeByUrl } from '../../utils/utils'; import { getRuntimeTxDetail, getSubmitStatus } from '../api'; -import { buildParatimeTxBody, buildTxBody, getChainContext, submitTx } from '../api/txHelper'; +import { getNowUrl } from '../api/request'; +import { buildParatimeTxBody, buildTxBody, getChainContext, removeLocalApproveAndDepositTransactionCacheByHash, setLocalApproveTransactionCache, setLocalDepositTransactionCache, submitTx } from '../api/txHelper'; import { get, removeValue, save } from '../storage/storageService'; const EthUtils = require('ethereumjs-util'); @@ -702,30 +703,34 @@ class APIService { */ setAllowanceAndDepositToParatimeAccount= (params)=>{ return new Promise( async (resolve,reject)=>{ - let allowanceDifference = new BigNumber(params.amount).minus(params.allowance).toString() - if(new BigNumber(allowanceDifference).lte(0)){ - params.method = TRANSACTION_TYPE.StakingAllow - return await this.depositToParatimeAccount(params,resolve) - }else{ - params.allowance = allowanceDifference - const tw = oasis.staking.allowWrapper() - params.method = TRANSACTION_TYPE.StakingAllow - params.toAddress = oasis.staking.addressToBech32(await oasis.staking.addressFromRuntimeID(oasis.misc.fromHex(params.runtimeId))) - let result = await this.submitTxBody(params, tw,true,(data)=>this.depositToParatimeAccount(params,resolve,reject,data)).catch(err=>err) - if(result&&result.error){ - reject({error:result.error}) - } + params.allowance = new BigNumber(params.amount).toString() + const tw = oasis.staking.allowWrapper() + params.method = TRANSACTION_TYPE.StakingAllow + params.toAddress = oasis.staking.addressToBech32(await oasis.staking.addressFromRuntimeID(oasis.misc.fromHex(params.runtimeId))) + let result = await this.submitTxBody(params, tw,true,(data,approveTxHash,currentNetConfig)=>this.depositToParatimeAccount(params,resolve,reject,data,approveTxHash,currentNetConfig)).catch(err=>err) + if(result&&result.error){ + let sendResult = {error:result.error} + showErrorNotification(params.fromAddress,sendResult) + reject(sendResult) + }else if(result && result.code === 0){ + resolve(result) } }) - } - depositToParatimeAccount= async(params,resolve,reject,data)=>{ + } + depositToParatimeAccount= async(params,resolve,reject,data,approveTxHash,currentNetConfig)=>{ if(data && data.code !== 0){ + showErrorNotification(params.fromAddress,data) reject(data) + approveTxHash && removeLocalApproveAndDepositTransactionCacheByHash([approveTxHash],currentNetConfig.netType) return } const consensusWrapper = new oasisRT.consensusAccounts.Wrapper(oasis.misc.fromHex(params.runtimeId)); const depositWrapper = consensusWrapper.callDeposit() - let submitRuntime = await this.submitParatimeAccountTx(params, depositWrapper).catch(err=>err) + let config = getRuntimeConfig(params.runtimeId) + if (config && config.accountType === RUNTIME_ACCOUNT_TYPE.EVM) { + params.approveTxHash = approveTxHash + } + let submitRuntime = await this.submitParatimeAccountTx(params, depositWrapper,currentNetConfig).catch(err=>err) resolve(submitRuntime) } setEvmWithdrawToConsensusAccount=(params)=>{ @@ -764,7 +769,7 @@ class APIService { let config = getRuntimeConfig(params.runtimeId) if (hash && config.accountType === RUNTIME_ACCOUNT_TYPE.EVM) { this.createNotificationAfterRuntimeTxSucceeds(hash,params.runtimeId) - console.log("params==0",params); + return { code:0, txHash:hash, @@ -784,9 +789,10 @@ class APIService { throw {error} } } - submitParatimeAccountTx= async(params, wrapper)=>{ + submitParatimeAccountTx= async(params, wrapper,currentNetConfig)=>{ + let config = getRuntimeConfig(params.runtimeId) try { - let buildResult = await buildParatimeTxBody(params,wrapper) + let buildResult = await buildParatimeTxBody(params,wrapper,currentNetConfig) let nonce = buildResult.nonce let txWrapper = buildResult.txWrapper let consensusChainContext = buildResult.consensusChainContext @@ -803,12 +809,12 @@ class APIService { }); txWrapper.setSignerInfo([signerInfo]); await txWrapper.sign([signer], consensusChainContext); - await submitTx(txWrapper, RETRY_TIME) + await submitTx(txWrapper, RETRY_TIME,currentNetConfig) let u8Hash = await oasis.hash.hash(txWrapper.unverifiedTransaction[0]) let hash = oasis.misc.toHex(u8Hash) - let config = getRuntimeConfig(params.runtimeId) if (hash && config.accountType === RUNTIME_ACCOUNT_TYPE.EVM) { - this.createNotificationAfterRuntimeTxSucceeds(hash,params.runtimeId) + params.approveTxHash && currentNetConfig && setLocalDepositTransactionCache(params.fromAddress, params.approveTxHash,params.runtimeId,hash, currentNetConfig.netType) + this.createNotificationAfterRuntimeTxSucceeds(hash,params.runtimeId,params.approveTxHash,currentNetConfig) return { code:0, txHash:hash, @@ -825,10 +831,12 @@ class APIService { } return {code:0} } catch (error) { + if(config.accountType === RUNTIME_ACCOUNT_TYPE.EVM) { + params.approveTxHash && removeLocalApproveAndDepositTransactionCacheByHash([params.approveTxHash],currentNetConfig.netType) + } throw {error} } } - /** * submit tx * @param {*} params @@ -868,6 +876,13 @@ class APIService { } if(!callback){ return sendResult + }else{ + let config = getRuntimeConfig(params.runtimeId) + if (config && config.accountType === RUNTIME_ACCOUNT_TYPE.EVM) { + let netType = getCurrentNetConfig().netType + setLocalApproveTransactionCache(sendResult.from,sendResult.hash,netType) + return {code:0} + } } } catch (error) { throw {error} @@ -880,10 +895,11 @@ class APIService { extension.notifications.onClicked.addListener(function (clickId) { if(myNotificationID === clickId){ let url + let explorerUrl = getCurrentNetConfig().explorer if(runtimeId){ - url = getExplorerUrl() + "paratimes/transactions/" + clickId + url = explorerUrl + "paratimes/transactions/" + clickId }else{ - url = getExplorerUrl() + "transactions/" + clickId + url = explorerUrl + "transactions/" + clickId } openTab(url) } @@ -901,52 +917,66 @@ class APIService { return } checkTxStatus = (hash,hideNotify,callback) => { - this.fetchTransactionStatus(hash,hideNotify,callback) + let real = getNowUrl() + let fetchUrl = real.url + this.fetchTransactionStatus(hash,hideNotify,fetchUrl,callback) } - onSuccess=(data,hash,hideNotify,callback)=>{ + onSuccess=(currentNetConfig,data,hash,hideNotify,callback)=>{ if(!hideNotify){ this.notification(hash) } if(callback){ try { let rawData = JSON.parse(data.raw) - callback(rawData.error) + callback(rawData.error,hash,currentNetConfig) } catch (error) { callback({error}) } } - } - fetchTransactionStatus = (hash,hideNotify,callback) => { - getSubmitStatus(hash).then((data) => { + } + fetchTransactionStatus = (hash,hideNotify,baseFetchUrl,callback) => { + getSubmitStatus(hash,baseFetchUrl).then((data) => { if (data && data.txHash) { - extension.runtime.sendMessage({ - type: FROM_BACK_TO_RECORD, - action: TX_SUCCESS, - data - }); - this.onSuccess(data,hash,hideNotify,callback) + if(!callback){ + extension.runtime.sendMessage({ + type: FROM_BACK_TO_RECORD, + action: TX_SUCCESS, + data + }); + } + + let currentNetConfig = getNetTypeByUrl(baseFetchUrl) + this.onSuccess(currentNetConfig,data,hash,hideNotify,callback) if (this.statusTimer[hash]) { clearTimeout(this.statusTimer[hash]); this.statusTimer[hash] = null; } } else { this.statusTimer[hash] = setTimeout(() => { - this.fetchTransactionStatus(hash,hideNotify,callback); + this.fetchTransactionStatus(hash,hideNotify,baseFetchUrl,callback); }, 5000); } }).catch((error) => { this.statusTimer[hash] = setTimeout(() => { - this.fetchTransactionStatus(hash,hideNotify,callback); + this.fetchTransactionStatus(hash,hideNotify,baseFetchUrl,callback); }, 5000); }) } - createNotificationAfterRuntimeTxSucceeds = (hash,runtimeId) => { - this.fetchRuntimeTxStatus(hash,runtimeId) + createNotificationAfterRuntimeTxSucceeds = (hash,runtimeId,approveTxHash,currentNetConfig) => { + this.fetchRuntimeTxStatus(hash,runtimeId,currentNetConfig,approveTxHash) } - fetchRuntimeTxStatus = (hash,runtimeId) => { - getRuntimeTxDetail(hash,runtimeId).then((data) => { + fetchRuntimeTxStatus = (hash,runtimeId,currentNetConfig,approveTxHash) => { + let baseFetchUrl = currentNetConfig?.url || "" + getRuntimeTxDetail(hash,runtimeId,baseFetchUrl).then((data) => { if (data && data.txHash) { this.notification(hash,runtimeId) + let removeCacheData = approveTxHash+"+"+runtimeId+"+"+ data.txHash + + let config = getRuntimeConfig(runtimeId) + if (config && config.accountType === RUNTIME_ACCOUNT_TYPE.EVM) { + approveTxHash && removeLocalApproveAndDepositTransactionCacheByHash([removeCacheData],currentNetConfig.netType) + } + extension.runtime.sendMessage({ type: FROM_BACK_TO_RECORD, action: TX_SUCCESS, @@ -958,12 +988,12 @@ class APIService { } } else { this.runtimeStatusTimer[hash] = setTimeout(() => { - this.fetchRuntimeTxStatus(hash,runtimeId); + this.fetchRuntimeTxStatus(hash,runtimeId,currentNetConfig,approveTxHash); }, 5000); } }).catch((error) => { this.runtimeStatusTimer[hash] = setTimeout(() => { - this.fetchRuntimeTxStatus(hash,runtimeId); + this.fetchRuntimeTxStatus(hash,runtimeId,baseFetchUrl,approveTxHash); }, 5000); }) } diff --git a/src/constant/storageKey.js b/src/constant/storageKey.js index 03aa04a9..52c14146 100644 --- a/src/constant/storageKey.js +++ b/src/constant/storageKey.js @@ -18,3 +18,6 @@ export const LANGUAGE_CONFIG = "LANGUAGE_CONFIG" */ export const ADDRESS_BOOK_CONFIG = "ADDRESS_BOOK_CONFIG" + +/** notify approve transaction */ +export const APPROVE_AND_DEPOSIT_TRANSACTION = "APPROVE_AND_DEPOSIT_TRANSACTION" \ No newline at end of file diff --git a/src/constant/types.js b/src/constant/types.js index 342726d1..1d009a44 100644 --- a/src/constant/types.js +++ b/src/constant/types.js @@ -291,3 +291,10 @@ export const FRAME_SEND_TRANSFER = "FRAME_SEND_TRANSFER" * RESET WALLET WHEN USER CLICK RESET BUTTON */ export const RESET_WALLET = "RESET_WALLET" + + +/** notify the message from background to popup */ +export const FROM_BACK_TO_POPUP = "FROM_BACK_TO_POPUP" + +/** notify approve tx update */ +export const APPROVE_TRANSACTION_UPDATE = "APPROVE_TRANSACTION_UPDATE" \ No newline at end of file diff --git a/src/i18n/en.json b/src/i18n/en.json index 43acb27a..7d9d8f49 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -281,5 +281,6 @@ "withdrawTitle":"Withdraw", "withdrawToAddress":"Withdraw Address", - "withdrawInfo":"Withdraw Info" + "withdrawInfo":"Withdraw Info", + "pendingTx":"Pending transaction ({{length}})" } diff --git a/src/i18n/zh_CN.json b/src/i18n/zh_CN.json index ac3b3d6d..b1f1ff0f 100644 --- a/src/i18n/zh_CN.json +++ b/src/i18n/zh_CN.json @@ -279,6 +279,6 @@ "withdrawTitle":"提现", "withdrawToAddress":"提现地址", - "withdrawInfo":"提现信息" - + "withdrawInfo":"提现信息", + "pendingTx":"处理中 ({{length}})" } diff --git a/src/popup/pages/Paratime/index.js b/src/popup/pages/Paratime/index.js index 4cf9c48a..c1c40335 100644 --- a/src/popup/pages/Paratime/index.js +++ b/src/popup/pages/Paratime/index.js @@ -1,12 +1,15 @@ import cx from "classnames"; +import extension from 'extensionizer'; import React from "react"; import { connect } from "react-redux"; import loadingCommon from "../../../assets/images/loadingCommon.gif"; import noHistory from "../../../assets/images/noHistory.png"; import wallet_send from "../../../assets/images/wallet_send.png"; import sendDisable from "../../../assets/images/wallet_send_disable.svg"; -import { getRpcBalance, getRpcRuntimeList, getRuntimeBalanceRaw } from "../../../background/api"; -import { SEND_PAGE_TYPE_RUNTIME_DEPOSIT, SEND_PAGE_TYPE_RUNTIME_WITHDRAW } from "../../../constant/types"; +import { getRpcBalance, getRpcRuntimeList, getRuntimeBalanceRaw, getRuntimeTxDetail } from "../../../background/api"; +import { getLocalApproveAndDepositTransactionCacheByAddress, removeLocalApproveAndDepositTransactionCacheByHash } from "../../../background/api/txHelper"; +import { RUNTIME_ACCOUNT_TYPE } from "../../../constant/paratimeConfig"; +import { APPROVE_TRANSACTION_UPDATE, FROM_BACK_TO_POPUP, SEND_PAGE_TYPE_RUNTIME_DEPOSIT, SEND_PAGE_TYPE_RUNTIME_WITHDRAW } from "../../../constant/types"; import { ACCOUNT_TYPE } from "../../../constant/walletType"; import { getLanguage } from "../../../i18n"; import { updateAccountLoading, updateRuntimeList } from "../../../reducers/accountReducer"; @@ -17,21 +20,79 @@ import Clock from "../../component/Clock"; import Toast from "../../component/Toast"; import WalletBar from "../../component/WalletBar"; import "./index.scss"; + +const GET_LOCAL_TRANSACTION_TYPE_INIT = "GET_LOCAL_TRANSACTION_TYPE_INIT" class Paratime extends React.Component { constructor(props) { super(props); this.state = { refreshing: false, loading: props.runtimeList.length === 0, - currentShowList: this.getShowRuntimeList() + currentShowList: this.getShowRuntimeList(), + currentApproveList:[] } this.isUnMounted = false; this.isRequest = false } async componentDidMount() { this.fetchData() + this.getLocalApproveTransaction(GET_LOCAL_TRANSACTION_TYPE_INIT) + this.startListener() + } + + + onMessage=(message, sender, sendResponse)=>{ + const { type, action, data } = message; + if (type === FROM_BACK_TO_POPUP && action === APPROVE_TRANSACTION_UPDATE) { + this.getLocalApproveTransaction() + sendResponse(); + } + return true; + } + + /** + * get local tx and loop tx, if success, remove local + */ + getLocalApproveTransaction=(type,netType)=>{ + const { currentAccount,netConfig} = this.props + let currentNetType = netType||netConfig.currentNetType + let currentApproveList = getLocalApproveAndDepositTransactionCacheByAddress(currentAccount.address,currentNetType) + currentApproveList = currentApproveList.filter(tx=>tx) + this.callSetState({ + currentApproveList:currentApproveList + },()=>{ + if(type === GET_LOCAL_TRANSACTION_TYPE_INIT){ + this.startPendingRequest() + } + }) + } + + startPendingRequest= async()=>{ + const { netConfig } = this.props + let currentNetType = netConfig.currentNetType + + let list = this.state.currentApproveList + for (let index = list.length-1 ; index >= 0; index--) { + let txHashData = list[index]; + let splitTxHash = txHashData.split("+") + if(splitTxHash.length==3){ + let data= await getRuntimeTxDetail(splitTxHash[2],splitTxHash[1]).catch(err=>{err}) + if (data && data.txHash) { + let newList = list.splice(0,index+1) + removeLocalApproveAndDepositTransactionCacheByHash(newList,currentNetType) + break + }else{ + continue + } + + } + } } + startListener = () => { + extension.runtime.onMessage.addListener(this.onMessage); + } + getShowRuntimeList = () => { const { currentAccount, evmRuntimeList, runtimeList } = this.props if (currentAccount.evmAddress) { @@ -42,6 +103,7 @@ class Paratime extends React.Component { } componentWillUnmount() { this.isUnMounted = true; + this.onMessage && extension.runtime.onMessage.removeListener(this.onMessage) } componentWillReceiveProps(nextProps) { if (nextProps.refreshAccountLoading && !this.state.loading) { @@ -57,6 +119,10 @@ class Paratime extends React.Component { currentShowList: list }) } + + if(nextProps.netConfig.currentNetType !== this.props.netConfig.currentNetType){ + this.getLocalApproveTransaction(GET_LOCAL_TRANSACTION_TYPE_INIT,nextProps.netConfig.currentNetType) + } } getRuntimeAllowance = (allowanceList, runtimeAddress) => { let runtimeAddressLow = runtimeAddress.toLowerCase() @@ -181,6 +247,21 @@ class Paratime extends React.Component { getRuntimeName = (item) => { return item.name } + + + renderPengTransactionStatus=(item)=>{ + let { currentAccount } = this.props + let pendingLength = this.state.currentApproveList.length + if(item.accountType === RUNTIME_ACCOUNT_TYPE.EVM && !currentAccount.evmAddress && pendingLength >0){ + return( +
{runtimeName+" "}{showId}
+ {this.renderPengTransactionStatus(item)} {disableToConsensus ||{getLanguage('availableBalance')}{' '} @@ -258,6 +340,7 @@ const mapStateToProps = (state) => ({ evmRuntimeList: state.accountInfo.evmRuntimeList, refreshAccountLoading: state.accountInfo.refreshAccountLoading, currentAccount: state.accountInfo.currentAccount, + netConfig: state.network, }); function mapDispatchToProps(dispatch) { diff --git a/src/popup/pages/Paratime/index.scss b/src/popup/pages/Paratime/index.scss index aa5b09c0..ecf35b30 100644 --- a/src/popup/pages/Paratime/index.scss +++ b/src/popup/pages/Paratime/index.scss @@ -46,4 +46,13 @@ .runtimeListContainer { margin-top: 10px; } + .runtimePendingContainer{ + display: flex; + align-items: center; + margin: 4px 0; + .pending-loading{ + margin-left: 10px; + width: 20px; + } + } } diff --git a/src/popup/pages/Record/index.js b/src/popup/pages/Record/index.js index 7f4da036..a8dd8e64 100644 --- a/src/popup/pages/Record/index.js +++ b/src/popup/pages/Record/index.js @@ -13,7 +13,7 @@ import { FROM_BACK_TO_RECORD, TX_SUCCESS } from '../../../constant/types'; import { TRANSACTION_TYPE } from "../../../constant/walletType"; import { getLanguage } from "../../../i18n"; import { openTab } from '../../../utils/commonMsg'; -import { addressSlice, copyText, getExplorerUrl, isNumber } from "../../../utils/utils"; +import { addressSlice, copyText, getCurrentNetConfig, isNumber } from "../../../utils/utils"; import CustomView from "../../component/CustomView"; import Toast from "../../component/Toast"; import "./index.scss"; @@ -67,7 +67,8 @@ class Record extends React.Component { startListener = () => { extension.runtime.onMessage.addListener(async (message, sender, sendResponse) => { const { type, action, data } = message; - if (type === FROM_BACK_TO_RECORD && action === TX_SUCCESS && data.txHash === this.state.txDetail.txHash) { + let hash = this.state.txDetail.txHash || this.state.txDetail.hash + if (type === FROM_BACK_TO_RECORD && action === TX_SUCCESS && data.txHash === hash) { if(data.runtimeId){ this.callSetState({ txStatus: data.result, @@ -124,10 +125,11 @@ class Record extends React.Component { goToExplorer = () => { let hash = this.state.txDetail.txHash || this.state.txDetail.hash let url + let explorerUrl = getCurrentNetConfig().explorer if (this.state.isEvmTx) { - url = getExplorerUrl() + "paratimes/transactions/" + hash + "?runtime=" + this.state.txDetail.runtimeId + url = explorerUrl + "paratimes/transactions/" + hash + "?runtime=" + this.state.txDetail.runtimeId } else { - url = getExplorerUrl() + "transactions/" + hash + url = explorerUrl + "transactions/" + hash } openTab(url) } diff --git a/src/popup/pages/Send/index.js b/src/popup/pages/Send/index.js index bd86495f..ee7bd388 100644 --- a/src/popup/pages/Send/index.js +++ b/src/popup/pages/Send/index.js @@ -18,7 +18,7 @@ import { updateAddressBookFrom, updateNetConfigRequest } from "../../../reducers import { updateNetConfigList } from "../../../reducers/network"; import { sendMsg } from "../../../utils/commonMsg"; import { checkLedgerConnect } from "../../../utils/ledger"; -import { addressSlice, amountDecimals, getDisplayAmount, getNumberDecimals, isNumber, isTrueNumber, toNonExponential, trimSpace } from "../../../utils/utils"; +import { addressSlice, amountDecimals, getDisplayAmount, getNumberDecimals, getSendTxError, isNumber, isTrueNumber, toNonExponential, trimSpace } from "../../../utils/utils"; import { addressValid, evmAddressValid } from "../../../utils/validator"; import AccountIcon from "../../component/AccountIcon"; import Button from "../../component/Button"; @@ -692,11 +692,7 @@ class SendPage extends React.Component { } }) } else { - let errMessage = - data?.message || - data?.error?.message || - data?.error?.metadata?.['grpc-message'] || - getLanguage('postFailed') + let errMessage = getSendTxError(data) Toast.info(errMessage) return } diff --git a/src/popup/pages/Wallet/index.js b/src/popup/pages/Wallet/index.js index 34a77942..e6b8747b 100644 --- a/src/popup/pages/Wallet/index.js +++ b/src/popup/pages/Wallet/index.js @@ -18,7 +18,7 @@ import { updateAccountTx, updateCurrentAccount, updateNetAccount, updateRpcNonce import { setAccountInfo, updateDappConnectList, updateNetConfigRequest, updateSendPageType } from "../../../reducers/cache"; import { updateNetConfigList } from "../../../reducers/network"; import { openTab, sendMsg } from '../../../utils/commonMsg'; -import { addressSlice, connectAccountDataFilter, copyText, getExplorerUrl, isNumber } from "../../../utils/utils"; +import { addressSlice, connectAccountDataFilter, copyText, getCurrentNetConfig, isNumber } from "../../../utils/utils"; import Button from "../../component/Button"; import Clock from "../../component/Clock"; import TestModal from "../../component/TestModal"; @@ -505,7 +505,8 @@ class Wallet extends React.Component { return className } onClickGoExplorer = () => { - let url = getExplorerUrl() + "accounts/detail/" + this.props.currentAccount.address + let explorerUrl = getCurrentNetConfig().explorer + let url = explorerUrl + "accounts/detail/" + this.props.currentAccount.address openTab(url) } renderListExplorer = (index) => { diff --git a/src/utils/commonMsg.js b/src/utils/commonMsg.js index 4f2db5c2..f2f6b728 100644 --- a/src/utils/commonMsg.js +++ b/src/utils/commonMsg.js @@ -1,5 +1,7 @@ import extension from 'extensionizer' +import { getLanguage } from "../i18n" import { QUERY_TAB_TYPE } from '../constant/specifyType'; +import { getSendTxError } from './utils'; /** * Encapsulation class for sending messages * @param {*} message @@ -69,3 +71,17 @@ export function getActiveTab(type, params) { }) }) } + + +export function showErrorNotification(address, err) { + let errMessage = getSendTxError(err) + let title = getLanguage('notificationTitle') + let message = address+" :"+errMessage + let notifyId =address + Date.now() + extension.notifications.create(notifyId, { + title: title, + message: message, + iconUrl: '/img/oasis.png', + type: 'basic' + }); +} \ No newline at end of file diff --git a/src/utils/utils.js b/src/utils/utils.js index 500287ae..4514789e 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -6,6 +6,7 @@ import { NETWORK_CONFIG } from "../constant/storageKey"; import { PARATIME_CONFIG } from "../constant/paratimeConfig"; import * as oasis from '@oasisprotocol/client'; import * as oasisRT from '@oasisprotocol/client-rt'; +import { getLanguage } from "../i18n" /** * Address interception * @param {*} address @@ -179,18 +180,22 @@ export function getNumberDecimals(number) { } } -export function getExplorerUrl() { - let localNetConfig = getLocal(NETWORK_CONFIG) +/** + * get current net config + * @returns + */ +export function getCurrentNetConfig() { + let localNetConfigStr = getLocal(NETWORK_CONFIG) let config = {} - if (localNetConfig) { - localNetConfig = JSON.parse(localNetConfig) + if (localNetConfigStr) { + let localNetConfig = JSON.parse(localNetConfigStr) let currentList = localNetConfig.currentNetList currentList = currentList.filter((item, index) => { return item.isSelect }) - config = currentList && currentList.length > 0 ? currentList[0] : "" + config = currentList && currentList.length > 0 ? currentList[0] : {} } - return config.explorer + return config } @@ -328,4 +333,49 @@ export function isEvmAddress(address){ return true } return false +} + +/** + * + * @returns + */ + export function getNetTypeByUrl(url) { + let localNetConfigStr = getLocal(NETWORK_CONFIG) + let config = {} + if (localNetConfigStr) { + let localNetConfig = JSON.parse(localNetConfigStr) + let totalNetList = localNetConfig.totalNetList + let filterList = totalNetList.filter((item, index) => { + return url.indexOf(item.url) !== -1 + }) + config = filterList && filterList.length > 0 ? filterList[0] : "" + } + return config +} + + +/** + * check url is have https or http + * @param {*} url + */ +export function isContainBaseUrl(url){ + if(url.indexOf("https://")!==-1 || url.indexOf("http://")!==-1){ + return true + }else{ + return false + } +} + +/** + * get send error + * @param {*} data + * @returns + */ +export function getSendTxError(data){ + let errMessage = + data?.message || + data?.error?.message || + data?.error?.metadata?.['grpc-message'] || + getLanguage('postFailed') + return errMessage } \ No newline at end of file