From 48f65871847afd158b47b5c351c633230a0346d6 Mon Sep 17 00:00:00 2001 From: Shrinath Prabhu Date: Mon, 13 Mar 2023 17:27:30 +0530 Subject: [PATCH 01/48] Add data: csp for font (#240) --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index 5afb488a..e8b2469d 100644 --- a/netlify.toml +++ b/netlify.toml @@ -8,7 +8,7 @@ X-XSS-Protection = "1; mode=block" X-Content-Type-Options = "nosniff" Content-Security-Policy = ''' - font-src 'self' https://*.cloudfront.net; + font-src 'self' https://*.cloudfront.net data:; img-src 'self' data: https:; script-src 'self' 'unsafe-inline' https://*.cloudfront.net *.google-analytics.com *.googletagmanager.com; style-src 'self' 'unsafe-inline' https://*.cloudfront.net; From 42f098cfd06d574ba9ad3b9633b5e9b2742aae50 Mon Sep 17 00:00:00 2001 From: Shrinath Prabhu Date: Mon, 13 Mar 2023 17:28:30 +0530 Subject: [PATCH 02/48] [AR-5812] Requests should open wallet automatically (#239) * Show wallet if permissioned requests are passed * Open popup if wallet actions require permission --- src/components/VJsonViewer.vue | 2 +- src/utils/requestManagement.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/VJsonViewer.vue b/src/components/VJsonViewer.vue index f08585c4..e235c732 100644 --- a/src/components/VJsonViewer.vue +++ b/src/components/VJsonViewer.vue @@ -14,7 +14,7 @@ defineProps() > - diff --git a/src/pages/NFTScreen.vue b/src/pages/NFTScreen.vue index d13f7ba7..8572340e 100644 --- a/src/pages/NFTScreen.vue +++ b/src/pages/NFTScreen.vue @@ -6,15 +6,12 @@ import { onBeforeRouteLeave } from 'vue-router' import AppLoader from '@/components/AppLoader.vue' import NFTView from '@/components/NFTView.vue' import UserWallet from '@/components/UserWallet.vue' -import { CHAIN_LIST } from '@/models/RpcConfigList' import { useAppStore } from '@/store/app' import { useParentConnectionStore } from '@/store/parentConnection' -import { useRpcStore } from '@/store/rpc' import { useUserStore } from '@/store/user' const userStore = useUserStore() const appStore = useAppStore() -const rpcStore = useRpcStore() const parentConnectionStore = useParentConnectionStore() const refreshState = ref(false) const loader = ref({ @@ -35,44 +32,8 @@ const helpOtherTabsLogin = () => { onMounted(async () => { helpOtherTabsLogin() - setRpcConfigs() - await getRpcConfig() }) -function showLoader(message) { - loader.value.show = true - loader.value.message = `${message}...` -} - -function hideLoader() { - loader.value.show = false - loader.value.message = '' -} - -function setRpcConfigs() { - if (!rpcStore.rpcConfigs) rpcStore.setRpcConfigs(CHAIN_LIST) -} - -async function getRpcConfig() { - try { - showLoader('Loading') - if (rpcStore.selectedRPCConfig.chainId) return - const parentConnectionInstance = await parentConnection?.promise - const rpcConfig = await parentConnectionInstance?.getRpcConfig() - if (rpcConfig) { - rpcStore.setSelectedRPCConfig({ - favicon: 'blockchain-icon', - isCustom: false, - ...rpcConfig, - }) - } - } catch (err) { - console.log({ err }) - } finally { - hideLoader() - } -} - async function handleRefresh() { refreshState.value = true } diff --git a/src/pages/loggedInView.vue b/src/pages/loggedInView.vue index 992ec3ba..2df864e0 100644 --- a/src/pages/loggedInView.vue +++ b/src/pages/loggedInView.vue @@ -170,11 +170,16 @@ async function getRpcConfig() { if ([40404, 40405].includes(Number(rpcConfig.chainId))) { rpcConfig = CHAIN_LIST[0] } - rpcStore.setSelectedRPCConfig({ - favicon: 'blockchain-icon', - isCustom: false, - ...rpcConfig, - }) + if (rpcConfig) { + const selectedChain = CHAIN_LIST.find( + (chain) => Number(chain.chainId) === Number(rpcConfig.chainId) + ) + rpcStore.setSelectedRPCConfig({ + favicon: selectedChain ? selectedChain.favicon : 'blockchain-icon', + isCustom: false, + ...rpcConfig, + }) + } } } catch (err) { console.log({ err }) diff --git a/src/store/rpc.ts b/src/store/rpc.ts index ce12dc5c..3353f30d 100644 --- a/src/store/rpc.ts +++ b/src/store/rpc.ts @@ -97,6 +97,11 @@ export const useRpcStore = defineStore('rpcStore', { setSelectedRPCConfig(cfg: RpcConfigWallet): void { this.selectedRPCConfig = cfg }, + setRpcConfig(config: RpcConfigWallet) { + if (this.rpcConfigs) { + this.rpcConfigs[Number(config.chainId)].rpcUrls = config.rpcUrls + } + }, setRpcConfigs(list: Array) { const configs = {} list.forEach((chainConfig) => { diff --git a/src/utils/requestManagement.ts b/src/utils/requestManagement.ts index f763d36e..bf4a24d9 100644 --- a/src/utils/requestManagement.ts +++ b/src/utils/requestManagement.ts @@ -1,6 +1,7 @@ // Todo: Find a better place for these functions import { AppMode } from '@arcana/auth' import { ethErrors, serializeError } from 'eth-rpc-errors' +import { ethers } from 'ethers' import { watch } from 'vue' import { useToast } from 'vue-toastification' @@ -13,7 +14,7 @@ import { useActivitiesStore } from '@/store/activities' import { useRequestStore } from '@/store/request' import { useRpcStore } from '@/store/rpc' import { useUserStore } from '@/store/user' -import { getAuthProvider } from '@/utils/getAuthProvider' +import { getRequestHandler } from '@/utils/requestHandlerSingleton' import { getStorage } from '@/utils/storageWrapper' import validatePopulateContractForNft from '@/utils/validateAndPopulateContractForNft' import validatePopulateContractForToken from '@/utils/validateAndPopulateContractForToken' @@ -85,7 +86,7 @@ async function switchChain(request, keeper) { result: `Chain changed to ${rpcStore.selectedRpcConfig.chainName}`, id: request.id, }) - // router.push({ name: 'home' }) + router.push({ name: 'home' }) } function isExistingRpcUrl(url) { @@ -98,8 +99,10 @@ function isExistingRpcUrl(url) { }) } -function isExistingChainId(chainId) { - return rpcStore.rpcConfigList.some((chain) => chain.chainId === chainId) +function isExistingChain(chainId) { + return rpcStore.rpcConfigList.find( + (chain) => Number(chain.chainId) === Number(chainId) + ) } function validateSwitchChainParams({ chainId }) { @@ -124,7 +127,7 @@ function validateSwitchChainParams({ chainId }) { return result } -function validateAddNetworkParams(networkInfo) { +async function validateAddNetworkParams(networkInfo) { const result: { isValid: boolean; error: unknown } = { isValid: false, error: null, @@ -146,13 +149,19 @@ function validateAddNetworkParams(networkInfo) { result.error = getEtherInvalidParamsError( `RPC URL - ${networkInfo.rpcUrls[0]} already exists, please use different one` ) - } else if (isExistingChainId(parseInt(networkInfo.chainId))) { - result.error = getEtherInvalidParamsError( - `Chain ID - ${networkInfo.chainId} already exists, please use different one` - ) } else { - result.error = '' - result.isValid = true + const provider = new ethers.providers.JsonRpcProvider( + networkInfo.rpcUrls[0] + ) + const chainId = await provider.getNetwork() + if (Number(chainId.chainId) !== Number(networkInfo.chainId)) { + result.error = getEtherInvalidParamsError( + `Incorrect combination of chainId and rpcUrl` + ) + } else { + result.error = '' + result.isValid = true + } } return result } @@ -175,27 +184,44 @@ async function validateAddNftParams(tokenType, params) { }) } -function addNetwork(request, keeper) { +async function addNetwork(request, keeper) { const { method, params } = request const networkInfo = params[0] const name: string = networkInfo.chainName || '' const rpcUrls: string[] = networkInfo.rpcUrls || [] - const chainId = parseInt(networkInfo.chainId) || 0 + const chainId = networkInfo.chainId const symbol: string = networkInfo.nativeCurrency.symbol || '' - - const payload = { - chainName: name, - chainId: String(chainId), - blockExplorerUrls: networkInfo.blockExplorerUrls, - rpcUrls: rpcUrls, - favicon: 'blockchain-icon', - isCustom: true, - nativeCurrency: { - symbol: symbol, - decimals: networkInfo.nativeCurrency.decimals || 18, - }, + const existingChain = isExistingChain(chainId) + if (existingChain) { + rpcStore.setRpcConfig({ + ...existingChain, + rpcUrls, + }) + rpcStore.setSelectedChainId(existingChain.chainId) + await getRequestHandler().setRpcConfig({ + ...existingChain, + chainId: Number(existingChain.chainId), + }) + } else { + const payload = { + chainName: name, + chainId, + blockExplorerUrls: networkInfo.blockExplorerUrls, + rpcUrls: rpcUrls, + favicon: 'blockchain-icon', + isCustom: true, + nativeCurrency: { + symbol: symbol, + decimals: networkInfo.nativeCurrency.decimals || 18, + }, + } + rpcStore.addNetwork(payload) + rpcStore.setSelectedChainId(payload.chainId) + await getRequestHandler().setRpcConfig({ + ...payload, + chainId: Number(payload.chainId), + }) } - rpcStore.addNetwork(payload) if (!reqStore.areRequestsPendingForApproval) { router.push({ name: 'home' }) } @@ -337,7 +363,7 @@ async function processRequest({ request, isPermissionGranted }, keeper) { async function handleRequest(request, requestStore, appStore, keeper) { if (request.method === 'wallet_addEthereumChain') { - const validationResponse = validateAddNetworkParams(request.params[0]) + const validationResponse = await validateAddNetworkParams(request.params[0]) if (!validationResponse.isValid) { await keeper.reply(request.method, { jsonrpc: '2.0', From 7986e4623aac3c2970e7a3f4a7d7cd36f20325fd Mon Sep 17 00:00:00 2001 From: Shrinath Prabhu Date: Thu, 16 Mar 2023 20:15:13 +0530 Subject: [PATCH 09/48] Close modal on add network (#245) --- src/components/AddNetwork.vue | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/AddNetwork.vue b/src/components/AddNetwork.vue index da571852..4f8e85e9 100644 --- a/src/components/AddNetwork.vue +++ b/src/components/AddNetwork.vue @@ -3,6 +3,7 @@ import { ethers } from 'ethers' import { ref } from 'vue' import { useToast } from 'vue-toastification' +import { useModalStore } from '@/store/modal' import { useRpcStore } from '@/store/rpc' import { getRequestHandler } from '@/utils/requestHandlerSingleton' import { useImage } from '@/utils/useImage' @@ -12,6 +13,7 @@ const emit = defineEmits(['close']) const rpcStore = useRpcStore() const getImage = useImage() const toast = useToast() +const modalStore = useModalStore() const rpcConfig = ref({ networkName: '', @@ -58,7 +60,14 @@ async function handleSubmit() { ...existingChain, rpcUrls: [rpcConfig.value.rpcUrl], }) + rpcStore.setSelectedChainId(existingChain.chainId) + await getRequestHandler().setRpcConfig({ + ...existingChain, + rpcUrls: [rpcConfig.value.rpcUrl], + chainId: Number(existingChain.chainId), + }) } else { + console.log('Adding custom network') const payload = { chainName: rpcConfig.value.networkName, chainId: rpcConfig.value.chainId, @@ -71,14 +80,16 @@ async function handleSubmit() { }, isCustom: true, } + console.log({ payload }) rpcStore.addNetwork(payload) rpcStore.setSelectedChainId(payload.chainId) await getRequestHandler().setRpcConfig({ ...payload, chainId: Number(payload.chainId), }) - emit('close') } + modalStore.setShowModal(false) + emit('close') } } From 9c8b67f2371892a08c75f06573f3a8ffced22bfd Mon Sep 17 00:00:00 2001 From: makylfang Date: Fri, 17 Mar 2023 11:20:31 +0400 Subject: [PATCH 10/48] Added login src and redirection to wallet for react-native src (#246) --- src/models/Connection.ts | 2 ++ src/pages/initPageV2.vue | 2 ++ src/pages/loginRedirect.vue | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/src/models/Connection.ts b/src/models/Connection.ts index 7293c550..5485db1a 100644 --- a/src/models/Connection.ts +++ b/src/models/Connection.ts @@ -49,6 +49,7 @@ type ProviderEvent = type RedirectParentConnectionApi = { redirect(url: string | null): Promise replyTo(parentAppUrl?: string | null): Promise + goToWallet(): Promise error(errorMessage: string, parentAppUrl: string): Promise } @@ -68,6 +69,7 @@ type ParentConnectionApi = { } type InitParentConnectionApi = { getParentUrl(): string + getLoginSource(): string getPasswordlessParams(): { sessionId: string; setToken: string } error(e: string): void } diff --git a/src/pages/initPageV2.vue b/src/pages/initPageV2.vue index f38909de..a744f8f8 100644 --- a/src/pages/initPageV2.vue +++ b/src/pages/initPageV2.vue @@ -57,7 +57,9 @@ async function init() { ) } else { const parentAppUrl = await parentConnectionInstance.getParentUrl() + const loginSrc = await parentConnectionInstance.getLoginSource() getStorage().local.setItem('parentAppUrl', parentAppUrl) + getStorage().local.setItem('loginSrc', loginSrc) } } finally { isLoading.value = false diff --git a/src/pages/loginRedirect.vue b/src/pages/loginRedirect.vue index 66801683..42b30557 100644 --- a/src/pages/loginRedirect.vue +++ b/src/pages/loginRedirect.vue @@ -36,6 +36,7 @@ onUnmounted(cleanup) async function init() { const storage = getStorage() const parentAppUrl = storage.local.getItem('parentAppUrl') + const loginSrc = storage.local.getItem('loginSrc') // TODO: Fix this V, throw error n stuff if (!parentAppUrl) { return @@ -150,6 +151,10 @@ async function init() { } ) } else { + if (loginSrc === 'rn') { + await connectionToParent.goToWallet() + return + } await handleSocialLogin( userInfo, messageId, From 3c2f10738bec807689ec9a863093ca2f7452cb5e Mon Sep 17 00:00:00 2001 From: Shrinath Prabhu Date: Fri, 17 Mar 2023 13:31:29 +0530 Subject: [PATCH 11/48] Fix issues related to switch network, edit network and delete network (#247) --- src/components/AddNetwork.vue | 15 ----------- src/components/EditNetwork.vue | 47 +++++++++++++++++++++------------- src/components/UserWallet.vue | 22 +++++++++++----- src/pages/loggedInView.vue | 2 +- src/store/rpc.ts | 4 ++- src/utils/requestManagement.ts | 14 ++-------- 6 files changed, 51 insertions(+), 53 deletions(-) diff --git a/src/components/AddNetwork.vue b/src/components/AddNetwork.vue index 4f8e85e9..b876dd13 100644 --- a/src/components/AddNetwork.vue +++ b/src/components/AddNetwork.vue @@ -3,9 +3,7 @@ import { ethers } from 'ethers' import { ref } from 'vue' import { useToast } from 'vue-toastification' -import { useModalStore } from '@/store/modal' import { useRpcStore } from '@/store/rpc' -import { getRequestHandler } from '@/utils/requestHandlerSingleton' import { useImage } from '@/utils/useImage' const emit = defineEmits(['close']) @@ -13,7 +11,6 @@ const emit = defineEmits(['close']) const rpcStore = useRpcStore() const getImage = useImage() const toast = useToast() -const modalStore = useModalStore() const rpcConfig = ref({ networkName: '', @@ -61,13 +58,7 @@ async function handleSubmit() { rpcUrls: [rpcConfig.value.rpcUrl], }) rpcStore.setSelectedChainId(existingChain.chainId) - await getRequestHandler().setRpcConfig({ - ...existingChain, - rpcUrls: [rpcConfig.value.rpcUrl], - chainId: Number(existingChain.chainId), - }) } else { - console.log('Adding custom network') const payload = { chainName: rpcConfig.value.networkName, chainId: rpcConfig.value.chainId, @@ -80,15 +71,9 @@ async function handleSubmit() { }, isCustom: true, } - console.log({ payload }) rpcStore.addNetwork(payload) rpcStore.setSelectedChainId(payload.chainId) - await getRequestHandler().setRpcConfig({ - ...payload, - chainId: Number(payload.chainId), - }) } - modalStore.setShowModal(false) emit('close') } } diff --git a/src/components/EditNetwork.vue b/src/components/EditNetwork.vue index a2385ece..b7fabee8 100644 --- a/src/components/EditNetwork.vue +++ b/src/components/EditNetwork.vue @@ -3,28 +3,31 @@ import { ref } from 'vue' import { useToast } from 'vue-toastification' import { useRpcStore } from '@/store/rpc' +import { getRequestHandler } from '@/utils/requestHandlerSingleton' import { useImage } from '@/utils/useImage' const emit = defineEmits(['close']) +const props = defineProps<{ + chainId: number +}>() const rpcStore = useRpcStore() const toast = useToast() const getImage = useImage() -const editChainId = rpcStore.editChainId -const rpcConfigForEdit = rpcStore.rpcConfigForEdit +const rpcConfigForEdit = rpcStore.getRpcConfig(props.chainId) const rpcConfig = ref({ chainName: rpcConfigForEdit?.chainName, chainId: rpcConfigForEdit?.chainId, rpcUrl: rpcConfigForEdit?.rpcUrls[0], currencySymbol: rpcConfigForEdit?.nativeCurrency?.symbol, - explorerUrl: rpcConfigForEdit?.blockExplorerUrls[0], + explorerUrl: rpcConfigForEdit?.blockExplorerUrls?.[0], }) -function isExistingRpcUrl(url) { +function isExistingRpcUrl(url: string) { const exisitingRpcUrls = rpcStore.rpcConfigList - .filter((chain) => chain.chainId !== editChainId) + .filter((chain) => Number(chain.chainId) !== props.chainId) .map((chain) => chain.rpcUrls) .flat() @@ -33,46 +36,54 @@ function isExistingRpcUrl(url) { }) } -function isExistingChainId(chainId) { +function isExistingChainId(chainId: number) { return rpcStore.rpcConfigList.some( - (chain) => chain.chainId === chainId && chain.chainId !== editChainId + (chain) => + Number(chain.chainId) === chainId && + Number(chain.chainId) !== Number(props.chainId) ) } function handleSubmit() { const rpcUrl = rpcConfig.value.rpcUrl const chainId = rpcConfig.value.chainId - if (isExistingRpcUrl(rpcUrl)) { + if (isExistingRpcUrl(rpcUrl as string)) { toast.error(`RPC URL - ${rpcUrl} already exists, please use different one`) - } else if (isExistingChainId(Number(chainId))) { + } else if (isExistingChainId(Number(rpcConfig.value.chainId))) { toast.error( `Chain ID - ${chainId} already exists, please use different one` ) } else { const payload = { chainName: rpcConfig.value.chainName, - chainId: Number(rpcConfig.value.chainId), - blockExplorerUrls: [rpcConfig.value.explorerUrl], - rpcUrls: [rpcConfig.value.rpcUrl], - favicon: rpcConfigForEdit?.favicon, + chainId: rpcConfig.value.chainId as string, + blockExplorerUrls: [rpcConfig.value.explorerUrl as string], + rpcUrls: [rpcConfig.value.rpcUrl as string], + favicon: rpcConfigForEdit?.favicon as string, isCustom: true, nativeCurrency: { - symbol: rpcConfig.value.currencySymbol, + symbol: rpcConfig.value.currencySymbol as string, decimals: 18, }, } - rpcStore.editNetwork(editChainId, payload) + rpcStore.editNetwork(props.chainId, payload) + if (Number(props.chainId) === Number(rpcStore.selectedRPCConfig.chainId)) { + getRequestHandler().setRpcConfig({ + ...payload, + chainId: Number(payload.chainId), + }) + } emit('close') } } function deleteNetwork() { - if (rpcStore.selectedChainId === editChainId) { + if (Number(rpcStore.selectedRpcConfig.chainId) === Number(props.chainId)) { toast.error( 'This network is current selected, please chose a different one and try again' ) } else { - rpcStore.deleteNetwork(editChainId) + rpcStore.deleteNetwork(props.chainId) emit('close') } } @@ -83,7 +94,7 @@ function deleteNetwork() {

Edit Network

-
+
+
diff --git a/src/pages/MFARestoreScreen.vue b/src/pages/MFARestoreScreen.vue index 5ac49541..e593c1fc 100644 --- a/src/pages/MFARestoreScreen.vue +++ b/src/pages/MFARestoreScreen.vue @@ -175,12 +175,12 @@ onUnmounted(() => {
- +
- We could not find the local MFA key information on this device.
- To recover, please answer the security questions or provide the PIN used - during the MFA setup. + Changing devices or clearing your browser data can remove access to your + key share stored in it. Recover it by answering the security questions of + enter PIN.
diff --git a/src/pages/MFASetup.vue b/src/pages/MFASetup.vue index f8e1a0ec..6d51fb82 100644 --- a/src/pages/MFASetup.vue +++ b/src/pages/MFASetup.vue @@ -27,6 +27,7 @@ const loader = ref({ }) const showPinScreen = ref(false) +const showSuccessScreen = ref(false) const showPinError = ref('') const pinToEncryptMFAShare = ref('') @@ -35,6 +36,7 @@ const securityQuestionModule = new SecurityQuestionModule(3) let globalQuestions: Ref = ref({}) const totalQuestions = 5 const selectedQuestions: CustomObject[] = new Array(totalQuestions) +const error: Ref = ref(new Array(totalQuestions).fill(false)) initStorage(String(route.params.appId)) @@ -75,6 +77,28 @@ function addSelectedQuestion(index: number, value: any) { selectedQuestions[index - 1] = { key: keyValue, customQuestion } } } + if (isQuestionRepeated(index)) { + error.value[index - 1] = true + } else { + error.value[index - 1] = false + } +} + +function getSelectedQuestion(index: number) { + if (selectedQuestions[index - 1]) { + return ( + selectedQuestions[index - 1]['customQuestion'] || + globalQuestions.value[selectedQuestions[index - 1]['key']] + ) + } + return '' +} + +function getAnswer(index: number) { + if (selectedQuestions[index - 1]) { + return selectedQuestions[index - 1]['value'] + } + return '' } function addAnswer(index: number, value: string) { @@ -124,6 +148,27 @@ function validatePin(pin?: string) { return returnValue } +function isQuestionRepeated(index: number) { + if (selectedQuestions[index - 1]?.key) { + for (let i = 0; i < selectedQuestions.length; i++) { + if (i !== index - 1) { + if (selectedQuestions[i]?.key === selectedQuestions[index - 1]?.key) { + return true + } + if (selectedQuestions[index - 1]?.customQuestion?.trim()) { + if ( + selectedQuestions[i]?.customQuestion?.trim() === + selectedQuestions[index - 1]?.customQuestion?.trim() + ) { + return true + } + } + } + } + } + return false +} + async function handleSubmit() { const isAllQuestionsAnswered = selectedQuestions.every( (question) => question.key?.trim() && question.value?.trim() @@ -163,7 +208,7 @@ async function handleSubmit() { showPinScreen.value = true } -async function handleDone() { +async function handlePinProceed() { const isPinValid = validatePin(pinToEncryptMFAShare.value) if (isPinValid) { @@ -182,11 +227,17 @@ async function handleDone() { show: false, message: '', } - // eslint-disable-next-line no-undef - connectionToParent.replyTo(process.env.VUE_APP_WALLET_DOMAIN) + showPinScreen.value = false + showSuccessScreen.value = true } } +async function handleDone() { + // eslint-disable-next-line no-undef + // eslint-disable-next-line no-undef + return connectionToParent.replyTo(process.env.VUE_APP_WALLET_DOMAIN) +} + function handleCancel() { return connectionToParent.error( 'User cancelled the setup', @@ -194,45 +245,32 @@ function handleCancel() { process.env.VUE_APP_WALLET_DOMAIN ) } + +function handlePinBack() { + showSuccessScreen.value = false + showPinScreen.value = false +}