From 022c3a6ff2647809745e93a857a252cc0e452b06 Mon Sep 17 00:00:00 2001 From: Tal Derei Date: Wed, 16 Oct 2024 08:38:51 -0700 Subject: [PATCH 1/6] handle non-mainnet chain id's --- .../src/routes/page/onboarding/success.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/extension/src/routes/page/onboarding/success.tsx b/apps/extension/src/routes/page/onboarding/success.tsx index 9f42b1e5..3c65caba 100644 --- a/apps/extension/src/routes/page/onboarding/success.tsx +++ b/apps/extension/src/routes/page/onboarding/success.tsx @@ -2,10 +2,23 @@ import { Button } from '@repo/ui/components/ui/button'; import { SplashPage } from '@repo/ui/components/ui/splash-page'; import { useStore } from '../../../state'; import { getDefaultFrontend } from '../../../state/default-frontend'; +import { localExtStorage } from '../../../storage/local'; +// import { useEffect } from 'react'; export const OnboardingSuccess = () => { const defaultFrontendUrl = useStore(getDefaultFrontend); + // Conditional: for beta-testing purposes, set the wallet block height to zero for non-mainnet chain id's. + void (async () => { + const storedParams = await localExtStorage.get('params'); + if (storedParams) { + const { chainId } = JSON.parse(storedParams as string); + if (chainId && !chainId.includes('penumbra-1')) { + await localExtStorage.set('walletCreationBlockHeight', 0); + } + } + })(); + return (
From bc8a258ce2b0153932c4adec212228b9beabdb4d Mon Sep 17 00:00:00 2001 From: Tal Derei Date: Wed, 16 Oct 2024 09:03:34 -0700 Subject: [PATCH 2/6] linting --- apps/extension/src/routes/page/onboarding/success.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/extension/src/routes/page/onboarding/success.tsx b/apps/extension/src/routes/page/onboarding/success.tsx index 3c65caba..0f20c6c3 100644 --- a/apps/extension/src/routes/page/onboarding/success.tsx +++ b/apps/extension/src/routes/page/onboarding/success.tsx @@ -3,7 +3,6 @@ import { SplashPage } from '@repo/ui/components/ui/splash-page'; import { useStore } from '../../../state'; import { getDefaultFrontend } from '../../../state/default-frontend'; import { localExtStorage } from '../../../storage/local'; -// import { useEffect } from 'react'; export const OnboardingSuccess = () => { const defaultFrontendUrl = useStore(getDefaultFrontend); @@ -12,8 +11,8 @@ export const OnboardingSuccess = () => { void (async () => { const storedParams = await localExtStorage.get('params'); if (storedParams) { - const { chainId } = JSON.parse(storedParams as string); - if (chainId && !chainId.includes('penumbra-1')) { + const parsedParams = JSON.parse(storedParams) as string; + if (parsedParams && !parsedParams.includes('penumbra-1')) { await localExtStorage.set('walletCreationBlockHeight', 0); } } From de587be5a0cea3a104c6019cbc0479bf5326e961 Mon Sep 17 00:00:00 2001 From: Tal Derei Date: Mon, 21 Oct 2024 00:19:53 -0700 Subject: [PATCH 3/6] migrate logic to grpc page --- .../page/onboarding/set-grpc-endpoint.tsx | 17 ++++++++++++++++- .../src/routes/page/onboarding/success.tsx | 12 ------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx b/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx index 61f6ba36..f3360c79 100644 --- a/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx +++ b/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx @@ -3,11 +3,26 @@ import { FadeTransition } from '@repo/ui/components/ui/fade-transition'; import { usePageNav } from '../../../utils/navigate'; import { PagePath } from '../paths'; import { GrpcEndpointForm } from '../../../shared/components/grpc-endpoint-form'; +import { localExtStorage } from '../../../storage/local'; +import { AppParameters } from '@penumbra-zone/protobuf/penumbra/core/app/v1/app_pb'; export const SetGrpcEndpoint = () => { const navigate = usePageNav(); - const onSuccess = (): void => { + // For beta testing: set the wallet block height to zero for non-mainnet chain IDs. + // This logic only runs after the user selects their rpc endpoint. + const onSuccess = async (): Promise => { + await new Promise(resolve => setTimeout(resolve, 1000)); + + const storedParams = await localExtStorage.get('params'); + if (storedParams) { + const parsedParams = JSON.parse(storedParams) as AppParameters; + if (!parsedParams.chainId.includes('penumbra-1')) { + await localExtStorage.set('walletCreationBlockHeight', 0); + } + } + + // Navigate to the next page after logic completes navigate(PagePath.SET_DEFAULT_FRONTEND); }; diff --git a/apps/extension/src/routes/page/onboarding/success.tsx b/apps/extension/src/routes/page/onboarding/success.tsx index 0f20c6c3..9f42b1e5 100644 --- a/apps/extension/src/routes/page/onboarding/success.tsx +++ b/apps/extension/src/routes/page/onboarding/success.tsx @@ -2,22 +2,10 @@ import { Button } from '@repo/ui/components/ui/button'; import { SplashPage } from '@repo/ui/components/ui/splash-page'; import { useStore } from '../../../state'; import { getDefaultFrontend } from '../../../state/default-frontend'; -import { localExtStorage } from '../../../storage/local'; export const OnboardingSuccess = () => { const defaultFrontendUrl = useStore(getDefaultFrontend); - // Conditional: for beta-testing purposes, set the wallet block height to zero for non-mainnet chain id's. - void (async () => { - const storedParams = await localExtStorage.get('params'); - if (storedParams) { - const parsedParams = JSON.parse(storedParams) as string; - if (parsedParams && !parsedParams.includes('penumbra-1')) { - await localExtStorage.set('walletCreationBlockHeight', 0); - } - } - })(); - return (
From fcd8e41e16de32ac4916e6a2ece2e48916543ea4 Mon Sep 17 00:00:00 2001 From: Tal Derei Date: Tue, 22 Oct 2024 13:26:52 -0700 Subject: [PATCH 4/6] simplify block height state --- .../page/onboarding/set-grpc-endpoint.tsx | 17 +---------------- apps/extension/src/storage/onboard.ts | 9 +++++++++ apps/extension/src/wallet-services.ts | 6 ++++-- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx b/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx index f3360c79..61f6ba36 100644 --- a/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx +++ b/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx @@ -3,26 +3,11 @@ import { FadeTransition } from '@repo/ui/components/ui/fade-transition'; import { usePageNav } from '../../../utils/navigate'; import { PagePath } from '../paths'; import { GrpcEndpointForm } from '../../../shared/components/grpc-endpoint-form'; -import { localExtStorage } from '../../../storage/local'; -import { AppParameters } from '@penumbra-zone/protobuf/penumbra/core/app/v1/app_pb'; export const SetGrpcEndpoint = () => { const navigate = usePageNav(); - // For beta testing: set the wallet block height to zero for non-mainnet chain IDs. - // This logic only runs after the user selects their rpc endpoint. - const onSuccess = async (): Promise => { - await new Promise(resolve => setTimeout(resolve, 1000)); - - const storedParams = await localExtStorage.get('params'); - if (storedParams) { - const parsedParams = JSON.parse(storedParams) as AppParameters; - if (!parsedParams.chainId.includes('penumbra-1')) { - await localExtStorage.set('walletCreationBlockHeight', 0); - } - } - - // Navigate to the next page after logic completes + const onSuccess = (): void => { navigate(PagePath.SET_DEFAULT_FRONTEND); }; diff --git a/apps/extension/src/storage/onboard.ts b/apps/extension/src/storage/onboard.ts index ac27eacb..0106a83e 100644 --- a/apps/extension/src/storage/onboard.ts +++ b/apps/extension/src/storage/onboard.ts @@ -46,3 +46,12 @@ export const onboardWallet = async (): Promise => { localExtStorage.addListener(storageListener); }); }; + +// For beta testing: set the wallet block height to zero for non-mainnet chain IDs. +// This logic only runs after the user selects their rpc endpoint. +export const handleWalletBlockHeight = async (grpcEndpoint: string) => { + await new Promise(resolve => setTimeout(resolve, 1000)); + if (!grpcEndpoint.includes('penumbra-1')) { + await localExtStorage.set('walletCreationBlockHeight', 0); + } +}; diff --git a/apps/extension/src/wallet-services.ts b/apps/extension/src/wallet-services.ts index affb8729..e666c760 100644 --- a/apps/extension/src/wallet-services.ts +++ b/apps/extension/src/wallet-services.ts @@ -4,7 +4,7 @@ import { createGrpcWebTransport } from '@connectrpc/connect-web'; import { createPromiseClient } from '@connectrpc/connect'; import { FullViewingKey, WalletId } from '@penumbra-zone/protobuf/penumbra/core/keys/v1/keys_pb'; import { localExtStorage } from './storage/local'; -import { onboardGrpcEndpoint, onboardWallet } from './storage/onboard'; +import { handleWalletBlockHeight, onboardGrpcEndpoint, onboardWallet } from './storage/onboard'; import { Services } from '@repo/context'; import { ServicesMessage } from './message/services'; import { WalletServices } from '@penumbra-zone/types/services'; @@ -15,13 +15,15 @@ export const startWalletServices = async () => { const wallet = await onboardWallet(); const grpcEndpoint = await onboardGrpcEndpoint(); const numeraires = await localExtStorage.get('numeraires'); + const chainId = await getChainId(grpcEndpoint); // Retrieve the wallet creation height flag from storage + await handleWalletBlockHeight(chainId); const walletCreationBlockHeight = await localExtStorage.get('walletCreationBlockHeight'); const services = new Services({ grpcEndpoint, - chainId: await getChainId(grpcEndpoint), + chainId, walletId: WalletId.fromJsonString(wallet.id), fullViewingKey: FullViewingKey.fromJsonString(wallet.fullViewingKey), numeraires: numeraires.map(n => AssetId.fromJsonString(n)), From 34767205171bfe9a0db676281419517e01d29013 Mon Sep 17 00:00:00 2001 From: Gabe Rodriguez Date: Wed, 23 Oct 2024 18:57:08 +0200 Subject: [PATCH 5/6] before hook suggestion (#220) --- .../page/onboarding/set-grpc-endpoint.tsx | 33 ++++++++++++++++++- .../components/grpc-endpoint-form/index.tsx | 4 ++- .../use-grpc-endpoint-form.ts | 15 +++++++-- apps/extension/src/storage/onboard.ts | 9 ----- apps/extension/src/wallet-services.ts | 5 +-- 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx b/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx index 61f6ba36..24bc94f8 100644 --- a/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx +++ b/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx @@ -3,6 +3,32 @@ import { FadeTransition } from '@repo/ui/components/ui/fade-transition'; import { usePageNav } from '../../../utils/navigate'; import { PagePath } from '../paths'; import { GrpcEndpointForm } from '../../../shared/components/grpc-endpoint-form'; +import { createPromiseClient } from '@connectrpc/connect'; +import { createGrpcWebTransport } from '@connectrpc/connect-web'; +import { AppService, TendermintProxyService } from '@penumbra-zone/protobuf'; +import { localExtStorage } from '../../../storage/local'; + +// Because the new seed phrase generation step queries a mainnet rpc, +// when using a non-mainnet chain id, there is a chance that generated wallet birthday is wrong. +// This logic fixes this issue after they select their rpc. +export const correctBirthdayHeightIfNeeded = async (grpcEndpoint: string) => { + const transport = createGrpcWebTransport({ baseUrl: grpcEndpoint }); + const { appParameters } = await createPromiseClient(AppService, transport).appParameters({}); + + if (!appParameters?.chainId.includes('penumbra-1')) { + const setWalletBirthday = await localExtStorage.get('walletCreationBlockHeight'); + if (setWalletBirthday) { + const tendermintClient = createPromiseClient(TendermintProxyService, transport); + const { syncInfo } = await tendermintClient.getStatus({}); + + // If the user's birthday is longer than the chain height, that means their mainnet birthday + // is too long and needs to be shortened to the current block height of the non-mainnet chain + if (syncInfo?.latestBlockHeight && Number(syncInfo.latestBlockHeight) < setWalletBirthday) { + await localExtStorage.set('walletCreationBlockHeight', Number(syncInfo.latestBlockHeight)); + } + } + } +}; export const SetGrpcEndpoint = () => { const navigate = usePageNav(); @@ -24,7 +50,12 @@ export const SetGrpcEndpoint = () => {
- +
diff --git a/apps/extension/src/shared/components/grpc-endpoint-form/index.tsx b/apps/extension/src/shared/components/grpc-endpoint-form/index.tsx index d85bd113..1752c721 100644 --- a/apps/extension/src/shared/components/grpc-endpoint-form/index.tsx +++ b/apps/extension/src/shared/components/grpc-endpoint-form/index.tsx @@ -15,10 +15,12 @@ export const GrpcEndpointForm = ({ submitButtonLabel, isOnboarding, onSuccess, + beforeSubmit, }: { submitButtonLabel: string; isOnboarding: boolean; onSuccess: () => void | Promise; + beforeSubmit?: (proposedEndpoint: string) => void | Promise; }) => { const { chainId, @@ -39,7 +41,7 @@ export const GrpcEndpointForm = ({ const handleSubmit = (e: FormEvent) => { e.preventDefault(); if (isSubmitButtonEnabled) { - void onSubmit(onSuccess); + void onSubmit({ beforeSubmit, onSuccess }); } }; diff --git a/apps/extension/src/shared/components/grpc-endpoint-form/use-grpc-endpoint-form.ts b/apps/extension/src/shared/components/grpc-endpoint-form/use-grpc-endpoint-form.ts index 977dd534..d961cf16 100644 --- a/apps/extension/src/shared/components/grpc-endpoint-form/use-grpc-endpoint-form.ts +++ b/apps/extension/src/shared/components/grpc-endpoint-form/use-grpc-endpoint-form.ts @@ -109,12 +109,21 @@ export const useGrpcEndpointForm = (isOnboarding: boolean) => { const chainIdChanged = !!originalChainId && !!chainId && originalChainId !== chainId; - const onSubmit = async ( + const onSubmit = async ({ + beforeSubmit, + onSuccess, + }: { + /** Callback to run prior to saving action */ + beforeSubmit?: (proposedEndpoint: string) => void | Promise; /** Callback to run when the RPC endpoint successfully saves */ - onSuccess: () => void | Promise, - ) => { + onSuccess: () => void | Promise; + }) => { setIsSubmitButtonEnabled(false); + if (beforeSubmit) { + await beforeSubmit(grpcEndpointInput); + } + // If the chain id has changed, our cache is invalid if (!isOnboarding && chainIdChanged) { const promiseWithResolvers = Promise.withResolvers(); diff --git a/apps/extension/src/storage/onboard.ts b/apps/extension/src/storage/onboard.ts index 0106a83e..ac27eacb 100644 --- a/apps/extension/src/storage/onboard.ts +++ b/apps/extension/src/storage/onboard.ts @@ -46,12 +46,3 @@ export const onboardWallet = async (): Promise => { localExtStorage.addListener(storageListener); }); }; - -// For beta testing: set the wallet block height to zero for non-mainnet chain IDs. -// This logic only runs after the user selects their rpc endpoint. -export const handleWalletBlockHeight = async (grpcEndpoint: string) => { - await new Promise(resolve => setTimeout(resolve, 1000)); - if (!grpcEndpoint.includes('penumbra-1')) { - await localExtStorage.set('walletCreationBlockHeight', 0); - } -}; diff --git a/apps/extension/src/wallet-services.ts b/apps/extension/src/wallet-services.ts index e666c760..9b55c8d0 100644 --- a/apps/extension/src/wallet-services.ts +++ b/apps/extension/src/wallet-services.ts @@ -4,7 +4,7 @@ import { createGrpcWebTransport } from '@connectrpc/connect-web'; import { createPromiseClient } from '@connectrpc/connect'; import { FullViewingKey, WalletId } from '@penumbra-zone/protobuf/penumbra/core/keys/v1/keys_pb'; import { localExtStorage } from './storage/local'; -import { handleWalletBlockHeight, onboardGrpcEndpoint, onboardWallet } from './storage/onboard'; +import { onboardGrpcEndpoint, onboardWallet } from './storage/onboard'; import { Services } from '@repo/context'; import { ServicesMessage } from './message/services'; import { WalletServices } from '@penumbra-zone/types/services'; @@ -16,9 +16,6 @@ export const startWalletServices = async () => { const grpcEndpoint = await onboardGrpcEndpoint(); const numeraires = await localExtStorage.get('numeraires'); const chainId = await getChainId(grpcEndpoint); - - // Retrieve the wallet creation height flag from storage - await handleWalletBlockHeight(chainId); const walletCreationBlockHeight = await localExtStorage.get('walletCreationBlockHeight'); const services = new Services({ From f3311a6cdb1bc050e35de50e72db89113bca24cc Mon Sep 17 00:00:00 2001 From: Tal Derei Date: Wed, 23 Oct 2024 21:33:38 -0700 Subject: [PATCH 6/6] extract logic and unit testing --- .../correct-wallet-birthday.test.ts | 46 +++++++++++++++++++ .../page/onboarding/set-grpc-endpoint.tsx | 17 +++++-- 2 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 apps/extension/src/routes/page/onboarding/correct-wallet-birthday.test.ts diff --git a/apps/extension/src/routes/page/onboarding/correct-wallet-birthday.test.ts b/apps/extension/src/routes/page/onboarding/correct-wallet-birthday.test.ts new file mode 100644 index 00000000..8d2917da --- /dev/null +++ b/apps/extension/src/routes/page/onboarding/correct-wallet-birthday.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect, vi } from 'vitest'; +import { adjustWalletBirthday } from './set-grpc-endpoint'; +import { localExtStorage } from '../../../storage/local'; + +describe('correctBirthdayHeightIfNeeded', () => { + it('should update the wallet birthday if the users wallet birthday is greater than the chain height', async () => { + const mockSet = vi.fn(); + vi.spyOn(localExtStorage, 'set').mockImplementation(mockSet); + await adjustWalletBirthday(1000, 900n); + + expect(mockSet).toHaveBeenCalledWith('walletCreationBlockHeight', 900); + }); + + it('should not update the wallet birthday if the users wallet birthday is less than the chain height', async () => { + const mockSet = vi.fn(); + vi.spyOn(localExtStorage, 'set').mockImplementation(mockSet); + await adjustWalletBirthday(900, 1000n); + + expect(mockSet).not.toHaveBeenCalled(); + }); + + it('should not update the wallet birthday if the users wallet birthday is equal to the chain height', async () => { + const mockSet = vi.fn(); + vi.spyOn(localExtStorage, 'set').mockImplementation(mockSet); + await adjustWalletBirthday(900, 900n); + + expect(mockSet).not.toHaveBeenCalled(); + }); + + it('should not update the wallet birthday if the latestBlockHeight is undefined', async () => { + const mockSet = vi.fn(); + vi.spyOn(localExtStorage, 'set').mockImplementation(mockSet); + await adjustWalletBirthday(900, undefined); + + expect(mockSet).not.toHaveBeenCalled(); + }); + + it('should not update if the wallet birthday is zero or negative', async () => { + const mockSet = vi.spyOn(localExtStorage, 'set').mockImplementation(() => Promise.resolve()); + + await adjustWalletBirthday(0, 900n); + await adjustWalletBirthday(-100, 900n); + + expect(mockSet).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx b/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx index 24bc94f8..0d4490db 100644 --- a/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx +++ b/apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx @@ -21,15 +21,22 @@ export const correctBirthdayHeightIfNeeded = async (grpcEndpoint: string) => { const tendermintClient = createPromiseClient(TendermintProxyService, transport); const { syncInfo } = await tendermintClient.getStatus({}); - // If the user's birthday is longer than the chain height, that means their mainnet birthday - // is too long and needs to be shortened to the current block height of the non-mainnet chain - if (syncInfo?.latestBlockHeight && Number(syncInfo.latestBlockHeight) < setWalletBirthday) { - await localExtStorage.set('walletCreationBlockHeight', Number(syncInfo.latestBlockHeight)); - } + await adjustWalletBirthday(setWalletBirthday, syncInfo?.latestBlockHeight); } } }; +// If the user's birthday is longer than the chain height, that means their mainnet birthday +// is too long and needs to be shortened to the current block height of the non-mainnet chain +export const adjustWalletBirthday = async ( + setWalletBirthday: number, + latestBlockHeight: bigint | undefined, +) => { + if (latestBlockHeight && Number(latestBlockHeight) < setWalletBirthday) { + await localExtStorage.set('walletCreationBlockHeight', Number(latestBlockHeight)); + } +}; + export const SetGrpcEndpoint = () => { const navigate = usePageNav();