From ef61cbda40ccf68a9b077fad17a528da6a31fadc Mon Sep 17 00:00:00 2001 From: Christopher Cali Date: Sun, 26 Nov 2023 15:36:52 -0500 Subject: [PATCH 1/9] Resolve .svg undefined error from browser console --- apps/web/src/layouts/default-layout.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web/src/layouts/default-layout.vue b/apps/web/src/layouts/default-layout.vue index 809677580..7d157652f 100644 --- a/apps/web/src/layouts/default-layout.vue +++ b/apps/web/src/layouts/default-layout.vue @@ -195,8 +195,8 @@ const toggleModal = (showModal: boolean) => { >
{{ convertString(user.address) }}
@@ -219,6 +219,7 @@ const toggleModal = (showModal: boolean) => { class="flex align-center items-center py-4 w-full cursor-default" > Date: Sun, 26 Nov 2023 20:15:47 -0500 Subject: [PATCH 2/9] Handle user rejecting sign tx and add detectActiveWallet to deposit, withdraw, register --- apps/web/src/composables/auth.ts | 22 ++----- apps/web/src/composables/staking.ts | 12 +++- apps/web/src/composables/wallets.ts | 13 ++++- apps/web/src/pages/operators/Operator.vue | 12 ++-- .../src/pages/overview/components/Staking.vue | 58 ++++++++++++------- 5 files changed, 68 insertions(+), 49 deletions(-) diff --git a/apps/web/src/composables/auth.ts b/apps/web/src/composables/auth.ts index 343c21595..89808cd48 100644 --- a/apps/web/src/composables/auth.ts +++ b/apps/web/src/composables/auth.ts @@ -6,6 +6,7 @@ import useLedger from "@/composables/ledger" import useTrezor from "@/composables/trezor" import useUser from "@/composables/user" import useWalletConnect from "@/composables/walletConnectV2" +import useWallets from "@/composables/wallets" import { Account, ApiResponse, @@ -15,11 +16,12 @@ import { } from "@casimir/types" const { usersUrl } = useEnvironment() -const { browserProvidersList, detectActiveEthersWalletAddress, loginWithEthers } = useEthers() +const { browserProvidersList, loginWithEthers } = useEthers() const { loginWithLedger } = useLedger() const { loginWithTrezor } = useTrezor() const { setUser, user } = useUser() const { loginWithWalletConnectV2, initializeWalletConnect, uninitializeWalletConnect } = useWalletConnect() +const { detectActiveWalletAddress } = useWallets() const initializedAuthComposable = ref(false) const loadingSessionLogin = ref(false) @@ -75,16 +77,6 @@ export default function useAuth() { } } - async function detectActiveWalletAddress(providerString: ProviderString) { - if (browserProvidersList.includes(providerString)) { - return await detectActiveEthersWalletAddress(providerString) - } else { - alert( - "detectActiveWalletAddress not yet implemented for this wallet provider" - ) - } - } - async function getUser() { try { const requestOptions = { @@ -177,17 +169,13 @@ export default function useAuth() { // If no, cancel/do nothing const hardwareWallet = provider === "Ledger" || provider === "Trezor" - const browserWallet = browserProvidersList.includes( - provider as ProviderString - ) + const browserWallet = browserProvidersList.includes(provider as ProviderString) if (hardwareWallet) { await loginWithProvider(loginCredentials as LoginCredentials) await getUser() return "Successfully logged in" } else if (browserWallet) { - const activeAddress = await detectActiveWalletAddress( - provider as ProviderString - ) + const activeAddress = await detectActiveWalletAddress(provider as ProviderString) if (activeAddress === address) { await loginWithProvider({ provider: provider as ProviderString, diff --git a/apps/web/src/composables/staking.ts b/apps/web/src/composables/staking.ts index 8fe7aa757..c74c8479a 100644 --- a/apps/web/src/composables/staking.ts +++ b/apps/web/src/composables/staking.ts @@ -22,6 +22,7 @@ const { getWalletConnectSignerV2 } = useWalletConnectV2() const stakingComposableInitialized = ref(false) const awaitingStakeOrWithdrawConfirmation = ref(false) const userStakeDetails = ref>([]) +const stakeWithdrawError = ref("") let baseManager: CasimirManager let eigenManager: CasimirManager @@ -47,6 +48,7 @@ export default function useStaking() { } async function deposit({ amount, walletProvider, type }: { amount: string, walletProvider: ProviderString, type: "default" | "eigen" }) { + stakeWithdrawError.value = "" try { const activeNetwork = await detectActiveNetwork(walletProvider) if (activeNetwork !== 5) { @@ -76,8 +78,9 @@ export default function useStaking() { const confirmation = await result.wait(1) if (confirmation) awaitingStakeOrWithdrawConfirmation.value = false return confirmation - } catch (err) { - console.error(`There was an error in deposit function: ${JSON.stringify(err)}`) + } catch (err: any) { + console.error(`Error in deposit function: ${JSON.stringify(err)}`) + stakeWithdrawError.value = err.reason awaitingStakeOrWithdrawConfirmation.value = false return false } @@ -175,6 +178,7 @@ export default function useStaking() { async function withdraw({ amount, walletProvider, type }: { amount: string, walletProvider: ProviderString, type: "default" | "eigen" }) { try { + stakeWithdrawError.value = "" const activeNetwork = await detectActiveNetwork(walletProvider) if (activeNetwork !== 5) { await switchEthersNetwork(walletProvider, "0x5") @@ -201,8 +205,9 @@ export default function useStaking() { const confirmation = await result.wait(1) if (confirmation) awaitingStakeOrWithdrawConfirmation.value = false return confirmation - } catch (err) { + } catch (err: any) { console.error(`There was an error in withdraw function: ${JSON.stringify(err)}`) + stakeWithdrawError.value = err.reason awaitingStakeOrWithdrawConfirmation.value = false return false } @@ -210,6 +215,7 @@ export default function useStaking() { return { awaitingStakeOrWithdrawConfirmation: readonly(awaitingStakeOrWithdrawConfirmation), + stakeWithdrawError: readonly(stakeWithdrawError), stakingComposableInitialized, userStakeDetails, initializeStakingComposable, diff --git a/apps/web/src/composables/wallets.ts b/apps/web/src/composables/wallets.ts index d7a05cada..823ef48ff 100644 --- a/apps/web/src/composables/wallets.ts +++ b/apps/web/src/composables/wallets.ts @@ -3,7 +3,7 @@ import { ProviderString } from "@casimir/types" import useEthers from "@/composables/ethers" const installedWallets = ref([] as ProviderString[]) -const { browserProvidersList, getBrowserProvider } = useEthers() +const { browserProvidersList, detectActiveEthersWalletAddress, getBrowserProvider } = useEthers() export default function useWallets() { async function detectActiveNetwork(providerString: ProviderString): Promise { @@ -30,6 +30,16 @@ export default function useWallets() { return await new Promise(resolve => resolve(0)) } } + + async function detectActiveWalletAddress(providerString: ProviderString) { + if (browserProvidersList.includes(providerString)) { + return await detectActiveEthersWalletAddress(providerString) + } else { + alert( + "detectActiveWalletAddress not yet implemented for this wallet provider" + ) + } + } async function detectInstalledWalletProviders() { @@ -86,6 +96,7 @@ export default function useWallets() { return { installedWallets: readonly(installedWallets), detectActiveNetwork, + detectActiveWalletAddress, detectInstalledWalletProviders, switchEthersNetwork } diff --git a/apps/web/src/pages/operators/Operator.vue b/apps/web/src/pages/operators/Operator.vue index eef0d7428..4d807d0bf 100644 --- a/apps/web/src/pages/operators/Operator.vue +++ b/apps/web/src/pages/operators/Operator.vue @@ -5,14 +5,13 @@ import { ProviderString } from "@casimir/types" import useAuth from "@/composables/auth" import useContracts from "@/composables/contracts" import useEnvironment from "@/composables/environment" -// import useEthers from '@/composables/ethers' import useFormat from "@/composables/format" import useOperators from "@/composables/operators" import useUser from "@/composables/user" +import useWallets from "@/composables/wallets" const { loadingSessionLogin } = useAuth() const { docsUrl } = useEnvironment() -// const { detectActiveWalletAddress } = useEthers() const { contractsAreInitialized } = useContracts() const { convertString } = useFormat() const { @@ -25,6 +24,7 @@ const { loadingAddOperator } = useOperators() const { user } = useUser() +const { detectActiveWalletAddress } = useWallets() // Form inputs const selectedWallet = ref<{ address: string, walletProvider: ProviderString }>({ address: "", walletProvider: "" }) @@ -285,10 +285,10 @@ async function submitRegisterOperatorForm() { const selectedAddress = selectedWallet.value.address const selectedProvider = selectedWallet.value.walletProvider - // const activeAddress = await detectActiveWalletAddress(selectedProvider) - // if (activeAddress !== selectedAddress) { - // return alert(`The account you selected is not the same as the one that is active in your ${selectedProvider} wallet. Please open your browser extension and select the account that you want to log in with.`) - // } + const activeAddress = await detectActiveWalletAddress(selectedProvider) + if (activeAddress !== selectedAddress) { + return alert(`The account you selected is not the same as the one that is active in your ${selectedProvider} wallet. Please open your ${selectedProvider} browser extension and select the account you want to use.`) + } try { await registerOperatorWithCasimir({ diff --git a/apps/web/src/pages/overview/components/Staking.vue b/apps/web/src/pages/overview/components/Staking.vue index 6e44e18bb..d1c684153 100644 --- a/apps/web/src/pages/overview/components/Staking.vue +++ b/apps/web/src/pages/overview/components/Staking.vue @@ -7,14 +7,16 @@ import useStaking from "@/composables/staking" import useFormat from "@/composables/format" import usePrice from "@/composables/price" import useUser from "@/composables/user" +import useWallets from "@/composables/wallets" import { ethers } from "ethers" import TermsOfService from "@/components/TermsOfService.vue" const { batchProvider } = useEnvironment() -const { stakingComposableInitialized, userStakeDetails, deposit, withdraw, getWithdrawableBalance } = useStaking() +const { stakeWithdrawError, stakingComposableInitialized, userStakeDetails, deposit, withdraw, getWithdrawableBalance } = useStaking() const { convertString, formatEthersCasimir, formatEthersCasimirStaking, parseEthersCasimir } = useFormat() const { getCurrentPrice } = usePrice() const { user, updateUserAgreement } = useUser() +const { detectActiveWalletAddress } = useWallets() // Staking Component Refs const addressBalance = ref(null) @@ -114,15 +116,10 @@ function toggleEstimatedAPY() { function selectAmountInput() { const inputElement = document.getElementById("amount_input") as HTMLInputElement - if (inputElement) { - inputElement.setSelectionRange(0, inputElement.value.length) - - // For mobile devices - inputElement.select() + inputElement.select() // For mobile devices } - } function aggregateAddressesByProvider() { @@ -221,11 +218,14 @@ function handleInputOnAmountToStakeOrWithdraw(event: any) { async function handleStake() { stakeButtonText.value = "Staking..." - // const activeAddress = await detectActiveWalletAddress(selectedStakingProvider.value) - // if (activeAddress !== selectedWalletAddress.value) { - // formattedAmountToStakeOrWithdraw.value = '' - // return alert(`The account you selected is not the same as the one that is active in your ${selectedStakingProvider.value} wallet. Please open your browser extension and select the account that you want to log in with.`) - // } + // TODO: @DemogorGod - Should we check for active wallet here or in the staking.ts composable? + const activeAddress = await detectActiveWalletAddress(selectedStakingProvider.value) + if (activeAddress !== selectedWalletAddress.value) { + formattedAmountToStakeOrWithdraw.value = 0 + stakeButtonText.value = "Stake" + return alert(`The account you selected is not the same as the one that is active in your ${selectedStakingProvider.value} wallet. Please open your ${selectedStakingProvider.value} browser extension select the account you want to use to stake.`) + } + eigenIsToggled.value = false const result = await deposit({ amount: formattedAmountToStakeOrWithdraw.value.toString(), @@ -233,26 +233,25 @@ async function handleStake() { type: stakeType.value }) - if (result === false) stakeButtonText.value = "User Rejected Signature" - else stakeButtonText.value = "Staked!" - setTimeout(() => { stakeButtonText.value = "Stake" formattedAmountToStakeOrWithdraw.value = 0 }, 1000) if (result) alert("Your Stake Has Been Deposited!") - else alert("Your Stake Action Has Failed, Please Try Again Later!") } async function handleWithdraw() { stakeButtonText.value = "Withdrawing..." selectedOperatorGroupStakeDetails.value = undefined - // const activeAddress = await detectActiveWalletAddress(selectedStakingProvider.value) - // if (activeAddress !== selectedWalletAddress.value) { - // formattedAmountToStakeOrWithdraw.value = '' - // return alert(`The account you selected is not the same as the one that is active in your ${selectedStakingProvider.value} wallet. Please open your browser extension and select the account that you want to log in with.`) - // } + + /// TODO: @DemogorGod - Should we check for active wallet here or in the staking.ts composable? + const activeAddress = await detectActiveWalletAddress(selectedStakingProvider.value) + if (activeAddress !== selectedWalletAddress.value) { + formattedAmountToStakeOrWithdraw.value = 0 + stakeButtonText.value = "Withdraw" + return alert(`The account you selected is not the same as the one that is active in your ${selectedStakingProvider.value} wallet. Please open your ${selectedStakingProvider.value} browser extension select the account you want to use to withdraw.`) + } const withdrawableBalance = await getWithdrawableBalance({ walletProvider: selectedStakingProvider.value, @@ -281,7 +280,6 @@ async function handleWithdraw() { }, 1000) if (confirmation) alert("Your Stake Has Been Withdrawn!") - else alert("Your Stake Action Has Failed, Please Try Again Later!") } watch(formattedWalletOptions, async () => { @@ -338,6 +336,22 @@ watch(selectedWalletAddress, async () => { } }) +watch(stakeWithdrawError, () => { + if (stakeWithdrawError.value === "") return + if (stakeWithdrawError.value === "user rejected transaction") { + alert("User rejected transaction. Try again and confirm signature.") + setTimeout(() => { + stakeOrWithdraw.value === "stake" ? stakeButtonText.value = "Stake" : stakeButtonText.value = "Withdraw" + }, 2000) + } else { + // TODO: Determine other errors to handle and handle here. + alert("Stake failed. Try again later.") + setTimeout(() => { + stakeOrWithdraw.value === "stake" ? stakeButtonText.value = "Stake" : stakeButtonText.value = "Withdraw" + }, 2000) + } +}) + watch(user, async () => { if (user.value?.id) { aggregateAddressesByProvider() From e0a0977a61513e2951abef5e59c965f4c0276739 Mon Sep 17 00:00:00 2001 From: Christopher Cali Date: Sun, 26 Nov 2023 20:47:33 -0500 Subject: [PATCH 3/9] Better handle the Ledger device Ethereum Goerli app not open error --- .../web/src/components/ConnectWalletsFlow.vue | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/apps/web/src/components/ConnectWalletsFlow.vue b/apps/web/src/components/ConnectWalletsFlow.vue index 45e331ddc..860fcb4ff 100644 --- a/apps/web/src/components/ConnectWalletsFlow.vue +++ b/apps/web/src/components/ConnectWalletsFlow.vue @@ -50,7 +50,7 @@ const props = defineProps({ const flowState = ref("select_provider") const errorMessage = ref(false) -const errorMassageText = ref("Something went wrong, please try again later.") +const errorMessageText = ref("Something went wrong, please try again later.") const walletProviderAddresses = ref([] as CryptoAddress[]) const selectProviderLoading = ref(false) const selectedProvider = ref(null as ProviderString | null) @@ -78,7 +78,7 @@ async function handleConfirmCreateAccountWithExistingSecondary() { } else if (response === "Selected address is not active address in wallet") { flowState.value = "select_address" errorMessage.value = true - errorMassageText.value = "Address selected is not active." + errorMessageText.value = "Address selected is not active." } else if (response === "Error in userAuthState") { flowState.value = "connection_failed" setTimeout(() => { @@ -87,7 +87,7 @@ async function handleConfirmCreateAccountWithExistingSecondary() { }, 1000) } else { errorMessage.value = true - errorMassageText.value = "Something went wrong, please try again later." + errorMessageText.value = "Something went wrong, please try again later." } } @@ -109,7 +109,7 @@ async function selectAddress(address: string, pathIndex: number): Promise } else if (response === "Address already exists on this account") { flowState.value = "select_address" errorMessage.value = true - errorMassageText.value = "Address selected is already connected to your account." + errorMessageText.value = "Address selected is already connected to your account." } else if ( response === "Address already exists as a primary address on another account" || response === "Address already exists as a secondary address on another account" @@ -118,7 +118,7 @@ async function selectAddress(address: string, pathIndex: number): Promise } else if (response === "Selected address is not active address in wallet") { flowState.value = "select_address" errorMessage.value = true - errorMassageText.value = "Address selected is not active." + errorMessageText.value = "Address selected is not active." } else if (response === "Error in userAuthState") { flowState.value = "connection_failed" setTimeout(() => { @@ -127,7 +127,7 @@ async function selectAddress(address: string, pathIndex: number): Promise }, 1500) } else { errorMessage.value = true - errorMassageText.value = "Something went wrong, please try again later." + errorMessageText.value = "Something went wrong, please try again later." } } @@ -162,13 +162,26 @@ async function selectProvider(provider: ProviderString, currency: Currency = "ET } else { throw new Error("Provider not supported") } + errorMessage.value = false + errorMessageText.value = "" selectProviderLoading.value = false flowState.value = "select_address" } catch (error: any) { errorMessage.value = true - errorMassageText.value = "Something went wrong, please try again later." - selectProviderLoading.value = false - throw new Error(`Error selecting provider: ${error.message}`) + if (provider === "Ledger") { + const { message, name, statusCode } = error + if ( + message === "Ledger device: UNKNOWN_ERROR (0x6511)" + && name === "TransportStatusError" + && statusCode === 25873 + ) { + errorMessageText.value = "Unlock your Ledger and open Ethereum Goerli app." + selectProviderLoading.value = false + } + } else { + errorMessageText.value = "Something went wrong, please try again later." + selectProviderLoading.value = false + } } } @@ -260,7 +273,7 @@ onUnmounted(() => {
- Something went wrong, please try again later. + {{ errorMessageText }}
@@ -350,7 +363,7 @@ onUnmounted(() => {
- {{ errorMassageText }} + {{ errorMessageText }}
From 963aa1047d0d4c00f50fed4432b2cfd8b289e34f Mon Sep 17 00:00:00 2001 From: Christopher Cali Date: Mon, 27 Nov 2023 11:15:47 -0500 Subject: [PATCH 4/9] Use pathIndex to make sure the correct address is being used in Ledger --- .../web/src/components/ConnectWalletsFlow.vue | 13 +++++++--- apps/web/src/composables/auth.ts | 12 +++++---- apps/web/src/composables/ledger.ts | 3 ++- .../src/pages/overview/components/Staking.vue | 2 +- common/data/src/schemas/account.schema.json | 5 ++++ common/types/src/interfaces/Account.ts | 2 ++ services/users/src/providers/db.ts | 25 +++++++++++++------ services/users/src/providers/ethers.ts | 8 +++--- services/users/src/routes/auth.ts | 3 ++- services/users/src/routes/user.ts | 1 - 10 files changed, 50 insertions(+), 24 deletions(-) diff --git a/apps/web/src/components/ConnectWalletsFlow.vue b/apps/web/src/components/ConnectWalletsFlow.vue index 860fcb4ff..7bfa89814 100644 --- a/apps/web/src/components/ConnectWalletsFlow.vue +++ b/apps/web/src/components/ConnectWalletsFlow.vue @@ -95,10 +95,13 @@ async function handleConfirmCreateAccountWithExistingSecondary() { * Checks if user is adding an account or logging in * @param address */ -async function selectAddress(address: string, pathIndex: number): Promise { +async function selectAddress(address: string, pathIndex?: number): Promise { selectedAddress.value = address flowState.value = "loading" - const loginCredentials: LoginCredentials = { provider: selectedProvider.value as ProviderString, address, currency: "ETH", pathIndex } + const loginCredentials: LoginCredentials = + pathIndex !== undefined ? + { provider: selectedProvider.value as ProviderString, address, currency: "ETH", pathIndex } : + { provider: selectedProvider.value as ProviderString, address, currency: "ETH" } const response = await login(loginCredentials) if (response === "Successfully logged in" || response === "Successfully added account to user") { flowState.value = "success" @@ -349,7 +352,11 @@ onUnmounted(() => {