From e9a4af5d6f98b5cdd0c8ea736907f93b119f003c Mon Sep 17 00:00:00 2001 From: ouhuang Date: Fri, 18 Oct 2024 02:02:10 +0800 Subject: [PATCH] WIP:Feat/support keystone (#801) * feat: add keystone connect wallet support * feat: add keystone sign tx support and fix ui * fix: fix sign dapp tx in webview browser * fix: fix eslint error * fix: refactor camera scanner layout * fix: default select all account * update: update camera scanner layout style * fix: fix pr comments * fix: fix webview screen sleep2s to sign next tx * chore: rebase master and fix expo-camera bugs * fix: remove unused code * fix: add shim.js to fix Bug * fix: remove gross code * fix: fix scan qrcode the app crash * fix: update keystone account strorage provider --------- Co-authored-by: ZhenQian --- index.js | 1 + .../xcshareddata/swiftpm/Package.resolved | 21 +- package.json | 2 + src/App.tsx | 81 ++-- src/assets/images/connectKeystoneLogo.png | Bin 0 -> 1652 bytes src/assets/images/connectKeystoneLogo@2x.png | Bin 0 -> 3356 bytes src/assets/images/connectKeystoneLogo@3x.png | Bin 0 -> 5099 bytes src/assets/images/keystoneLogo.svg | 4 + src/assets/images/scannerLine.png | Bin 0 -> 9596 bytes src/assets/images/scannerLine@2x.png | Bin 0 -> 31153 bytes src/assets/images/scannerLine@3x.png | Bin 0 -> 63932 bytes src/assets/images/warningKeystone.svg | 5 + src/components/BackScreen.tsx | 3 +- src/components/CameraScannerLayout.tsx | 101 ++++ src/components/DynamicQrScanner.tsx | 98 ++++ src/components/StaticQrCode.tsx | 75 +++ src/features/account/AccountsScreen.tsx | 8 +- src/features/browser/BrowserWebViewScreen.tsx | 48 +- src/features/home/HomeNavigator.tsx | 5 + .../addNewAccount/AddNewAccountNavigator.tsx | 5 + .../addNewAccount/AddNewAccountScreen.tsx | 7 + .../home/addNewAccount/addNewAccountTypes.ts | 1 + src/features/home/homeTypes.ts | 1 + .../keystone/ConnectKeystoneStartScreen.tsx | 180 +++++++ .../keystone/KeystoneAccountAssignScreen.tsx | 222 +++++++++ src/features/keystone/KeystoneModal.tsx | 152 ++++++ src/features/keystone/KeystoneNavigator.tsx | 29 ++ .../keystone/KeystoneOnboardingProvider.tsx | 58 +++ src/features/keystone/ScanQrCodeScreen.tsx | 79 +++ .../keystone/SelectKeystoneAccountsScreen.tsx | 257 ++++++++++ src/features/keystone/SignTx/SignTxModal.tsx | 219 +++++++++ .../keystone/types/keystoneSolanaTxType.ts | 9 + src/features/migration/SolanaMigration.tsx | 4 +- .../onboarding/CreateImportAccountScreen.tsx | 14 + .../onboarding/OnboardingNavigator.tsx | 6 + .../create/createAccountNavTypes.ts | 1 + .../import/importAccountNavTypes.ts | 1 + src/features/onboarding/onboardingTypes.ts | 3 +- src/features/payment/PaymentCard.tsx | 8 +- src/hooks/useCamera.ts | 17 + src/locales/en.ts | 30 ++ src/navigation/RootNavigator.tsx | 14 + src/navigation/TabBarNavigator.tsx | 2 + src/navigation/rootTypes.ts | 3 + src/solana/SolanaProvider.tsx | 40 +- src/storage/AccountStorageProvider.tsx | 1 + src/storage/cloudStorage.ts | 22 +- yarn.lock | 453 +++++++++++++++++- 48 files changed, 2179 insertions(+), 111 deletions(-) create mode 100644 src/assets/images/connectKeystoneLogo.png create mode 100644 src/assets/images/connectKeystoneLogo@2x.png create mode 100644 src/assets/images/connectKeystoneLogo@3x.png create mode 100644 src/assets/images/keystoneLogo.svg create mode 100644 src/assets/images/scannerLine.png create mode 100644 src/assets/images/scannerLine@2x.png create mode 100644 src/assets/images/scannerLine@3x.png create mode 100644 src/assets/images/warningKeystone.svg create mode 100644 src/components/CameraScannerLayout.tsx create mode 100644 src/components/DynamicQrScanner.tsx create mode 100644 src/components/StaticQrCode.tsx create mode 100644 src/features/keystone/ConnectKeystoneStartScreen.tsx create mode 100644 src/features/keystone/KeystoneAccountAssignScreen.tsx create mode 100644 src/features/keystone/KeystoneModal.tsx create mode 100644 src/features/keystone/KeystoneNavigator.tsx create mode 100644 src/features/keystone/KeystoneOnboardingProvider.tsx create mode 100644 src/features/keystone/ScanQrCodeScreen.tsx create mode 100644 src/features/keystone/SelectKeystoneAccountsScreen.tsx create mode 100644 src/features/keystone/SignTx/SignTxModal.tsx create mode 100644 src/features/keystone/types/keystoneSolanaTxType.ts create mode 100644 src/hooks/useCamera.ts diff --git a/index.js b/index.js index 1c6897859..d36d631a1 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +import './shim' import { ThemeProvider } from '@shopify/restyle' import React from 'react' import { ErrorBoundary } from 'react-error-boundary' diff --git a/ios/HeliumWallet.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/HeliumWallet.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7ebcc34da..8c8230ba7 100644 --- a/ios/HeliumWallet.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/HeliumWallet.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "e70d3525c8e2819a8b34f22909815dab5c700c25a06c32388f3930f7b3627768", "pins" : [ { "identity" : "maplibre-gl-native-distribution", @@ -8,25 +9,7 @@ "revision" : "ffda61e298c1490d4860d5184e80d618aaadc089", "version" : "5.13.0" } - }, - { - "identity" : "swiftui-charts", - "kind" : "remoteSourceControl", - "location" : "https://github.com/spacenation/swiftui-charts", - "state" : { - "revision" : "b044e7eb04d0026490eecb115f4fc07197dad942", - "version" : "1.1.0" - } - }, - { - "identity" : "swiftui-shapes", - "kind" : "remoteSourceControl", - "location" : "https://github.com/spacenation/swiftui-shapes.git", - "state" : { - "revision" : "c58b15c37eae9bd20525c6daa93a06a689ca75cb", - "version" : "1.1.0" - } } ], - "version" : 2 + "version" : 3 } diff --git a/package.json b/package.json index 18ab4993b..eff00ef23 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@helium/voter-stake-registry-sdk": "0.9.7", "@helium/wallet-link": "4.11.0", "@jup-ag/api": "^6.0.6", + "@keystonehq/keystone-sdk": "^0.8.0", "@ledgerhq/hw-app-solana": "7.0.13", "@ledgerhq/react-native-hid": "6.30.0", "@ledgerhq/react-native-hw-transport-ble": "6.29.5", @@ -68,6 +69,7 @@ "@maplibre/maplibre-react-native": "^9.1.0", "@metaplex-foundation/mpl-bubblegum": "0.6.0", "@metaplex-foundation/mpl-token-metadata": "2.10.0", + "@ngraveio/bc-ur": "^1.1.13", "@onsol/tldparser": "^0.5.3", "@react-native-async-storage/async-storage": "1.18.1", "@react-native-community/blur": "4.3.0", diff --git a/src/App.tsx b/src/App.tsx index c026f40ea..0eaba2306 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -38,6 +38,7 @@ import { GovernanceProvider } from './storage/GovernanceProvider' import { useNotificationStorage } from './storage/NotificationStorageProvider' import { BalanceProvider } from './utils/Balance' import { useDeepLinking } from './utils/linking' +import KeystoneOnboardingProvider from './features/keystone/KeystoneOnboardingProvider' SplashLib.preventAutoHideAsync().catch(() => { /* reloading the app might trigger some race conditions, ignore them */ @@ -122,46 +123,48 @@ const App = () => { - - - - {accountsRestored && ( - <> - - - - - - - - - + + + + + {accountsRestored && ( + <> + + + + + + + + + - {/* place app specific modals here */} - - - - - - - - - - )} - - - + {/* place app specific modals here */} + + + + + + + + + + )} + + + + diff --git a/src/assets/images/connectKeystoneLogo.png b/src/assets/images/connectKeystoneLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..3e4239ea3d5d13501db707c4f2cadad1ed190384 GIT binary patch literal 1652 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 + + + \ No newline at end of file diff --git a/src/assets/images/scannerLine.png b/src/assets/images/scannerLine.png new file mode 100644 index 0000000000000000000000000000000000000000..c390a88211582ade3d1576630112a7ba24b25e08 GIT binary patch literal 9596 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 -Dw2aQ#9AwWT{3L0S9 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

4@w#5s+MD5a&7>s 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

FnV%x%Z^)nUK!{xU;G!tA^J0 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 a;r|Dj^{x574)C)80000 literal 0 HcmV?d00001 diff --git a/src/assets/images/warningKeystone.svg b/src/assets/images/warningKeystone.svg new file mode 100644 index 000000000..9b1341fba --- /dev/null +++ b/src/assets/images/warningKeystone.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/BackScreen.tsx b/src/components/BackScreen.tsx index eee02f9cf..7dd38f126 100644 --- a/src/components/BackScreen.tsx +++ b/src/components/BackScreen.tsx @@ -107,7 +107,8 @@ const BackScreen = ({ )} - + {/* if padding is not set, set it to 'lx' , if padding set to 'none' , set it to 0 */} + {children} diff --git a/src/components/CameraScannerLayout.tsx b/src/components/CameraScannerLayout.tsx new file mode 100644 index 000000000..187d82d66 --- /dev/null +++ b/src/components/CameraScannerLayout.tsx @@ -0,0 +1,101 @@ +import React, { useEffect } from 'react' +import { Image } from 'react-native' +import Animated, { + useSharedValue, + useAnimatedStyle, + withRepeat, + withTiming, +} from 'react-native-reanimated' +import Box from './Box' + +const SCANNER_SIZE = 300 +const SCANNER_LINE_HEIGHT = 43 +const SCAN_DURATION = 2000 +const BORDER_SEGMENT_SIZE = 40 +export const CameraScannerLayout = () => { + const linePosition = useSharedValue(-SCANNER_LINE_HEIGHT) + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ translateY: linePosition.value }], + })) + + useEffect(() => { + linePosition.value = withRepeat( + withTiming(SCANNER_SIZE, { + duration: SCAN_DURATION, + }), + -1, + ) + }, [linePosition]) + + return ( + + + {/* top left */} + + {/* top right */} + + {/* bottom left */} + + {/* bottom right */} + + {/* animated scanner line */} + + + + + + ) +} diff --git a/src/components/DynamicQrScanner.tsx b/src/components/DynamicQrScanner.tsx new file mode 100644 index 000000000..1402a3870 --- /dev/null +++ b/src/components/DynamicQrScanner.tsx @@ -0,0 +1,98 @@ +/* eslint-disable no-console */ +import React, { useEffect, useState } from 'react' +import { BarcodeScanningResult, Camera, CameraView } from 'expo-camera' +import { Linking, Platform, StyleSheet } from 'react-native' +import { useNavigation } from '@react-navigation/native' +import { useAsync } from 'react-async-hook' +import { useTranslation } from 'react-i18next' +import useAlert from '@hooks/useAlert' +import { CameraScannerLayout } from './CameraScannerLayout' +import Box from './Box' +import BackScreen from './BackScreen' +import ProgressBar from './ProgressBar' +import Text from './Text' + +type Props = { + progress: number + onBarCodeScanned: (data: string) => void +} +const DynamicQrScanner = ({ onBarCodeScanned, progress }: Props) => { + const [hasPermission, setHasPermission] = useState() + const navigation = useNavigation() + const { showOKCancelAlert } = useAlert() + const { t } = useTranslation() + + useEffect(() => { + Camera.requestCameraPermissionsAsync().then( + ({ status }: { status: string }) => { + setHasPermission(status === 'granted') + }, + ) + }, []) + + useAsync(async () => { + if (hasPermission !== false) return + + // if permission is not granted, show alert to open settings + const decision = await showOKCancelAlert({ + title: t('qrScanner.deniedAlert.title'), + message: t('qrScanner.deniedAlert.message'), + ok: t('qrScanner.deniedAlert.ok'), + }) + + // if user clicks ok, open settings + if (decision) { + if (Platform.OS === 'ios') { + Linking.openURL('app-settings:') + } else { + Linking.openSettings() + } + } + + // if user clicks cancel, go back to the previous screen + if (decision === false) { + navigation.goBack() + } + }, [hasPermission, navigation, showOKCancelAlert]) + + const handleBarCodeScanned = (result: BarcodeScanningResult) => { + onBarCodeScanned(result.data) + } + + return ( + + {/* if permission is not granted, show a black screen and notice alert modal */} + {hasPermission !== true && } + + {hasPermission === true && ( + + + + + + + + + + + {t('keystone.payment.scanTxQrcodeScreenSubtitle3')} + + + + )} + + ) +} +export default DynamicQrScanner diff --git a/src/components/StaticQrCode.tsx b/src/components/StaticQrCode.tsx new file mode 100644 index 000000000..4e785618d --- /dev/null +++ b/src/components/StaticQrCode.tsx @@ -0,0 +1,75 @@ +import React, { useEffect, useMemo, useState } from 'react' +import QRCode from 'react-native-qrcode-svg' +import { UR, UREncoder } from '@ngraveio/bc-ur' +import Box from './Box' + +const QR_CONTAINER_SIZE = 300 +const MAX_FRAGMENT_CAPACITY = 200 +export type StaticQrCodeProps = { + size?: number + ecl?: 'L' | 'M' | 'Q' | 'H' + quietZone?: number + data: string +} + +const StaticQrCode = ({ size, data, ecl, quietZone }: StaticQrCodeProps) => { + const qrCodeSize = size ?? QR_CONTAINER_SIZE + const qrCodeQuietZone = quietZone ?? 20 + const qrCodeEcl = ecl ?? 'L' + return ( + + + + ) +} + +export type AnimatedQrCodeProps = { + size?: number + ecl?: 'L' | 'M' | 'Q' | 'H' + quietZone?: number + refreshSpeed?: number + qrCodeType: string + cborData: string +} +const AnimatedQrCode = ({ + size, + cborData, + ecl, + quietZone, + qrCodeType, + refreshSpeed, +}: AnimatedQrCodeProps) => { + const qrCodeSize = size ?? QR_CONTAINER_SIZE + const qrCodeQuietZone = quietZone ?? 20 + const qrCodeEcl = ecl ?? 'L' + const qrRefreshSpeed = refreshSpeed ?? 200 + const urEncoder = useMemo(() => { + const ur = new UR(Buffer.from(cborData, 'hex'), qrCodeType) + return new UREncoder(ur, MAX_FRAGMENT_CAPACITY) + }, [cborData, qrCodeType]) + const firstUR = useMemo(() => urEncoder.nextPart(), [urEncoder]) + const [ur, setUR] = useState(firstUR) + + useEffect(() => { + const interval = setInterval(() => { + setUR(urEncoder.nextPart()) + }, qrRefreshSpeed) + return () => clearInterval(interval) + }, [urEncoder, qrRefreshSpeed]) + + return ( + + ) +} + +export { StaticQrCode, AnimatedQrCode } diff --git a/src/features/account/AccountsScreen.tsx b/src/features/account/AccountsScreen.tsx index 720d3c085..62e566c10 100644 --- a/src/features/account/AccountsScreen.tsx +++ b/src/features/account/AccountsScreen.tsx @@ -141,9 +141,15 @@ const AccountsScreen = () => { useEffect(() => { if (currentAccount?.ledgerDevice) return + // if current account is keystone account , check pass + if (currentAccount?.keystoneDevice) return const address = currentAccount?.address if (address) checkSecureAccount(address) - }, [currentAccount?.address, currentAccount?.ledgerDevice]) + }, [ + currentAccount?.address, + currentAccount?.ledgerDevice, + currentAccount?.keystoneDevice, + ]) useEffect(() => { if (openedNotification && !locked) { diff --git a/src/features/browser/BrowserWebViewScreen.tsx b/src/features/browser/BrowserWebViewScreen.tsx index 829c9549b..46aad86e8 100644 --- a/src/features/browser/BrowserWebViewScreen.tsx +++ b/src/features/browser/BrowserWebViewScreen.tsx @@ -271,39 +271,21 @@ const BrowserWebViewScreen = () => { return } - const signedTransactions = await Promise.all( - transactions.map( - async ({ - transaction, - }: SolanaSignAndSendTransactionInput & { - transaction: Transaction | VersionedTransaction - }) => { - let signedTransaction: - | Transaction - | VersionedTransaction - | undefined - if (!isVersionedTransaction) { - // TODO: Verify when lookup table is needed - // transaction.add(lookupTableAddress) - signedTransaction = - await anchorProvider?.wallet.signTransaction( - transaction as Transaction, - ) - } else { - signedTransaction = - await anchorProvider?.wallet.signTransaction( - transaction as VersionedTransaction, - ) - } - - if (!signedTransaction) { - throw new Error('Failed to sign transaction') - } - - return signedTransaction - }, - ), - ) + const signedTransactions: (Transaction | VersionedTransaction)[] = [] + // eslint-disable-next-line no-restricted-syntax + for (const txInput of transactions) { + try { + const { transaction } = txInput + const convertTx = isVersionedTransaction + ? (transaction as VersionedTransaction) + : (transaction as Transaction) + const signedTransaction = + await anchorProvider?.wallet.signTransaction(convertTx) + signedTransactions.push(signedTransaction) + } catch (e) { + throw new Error('Failed to sign transaction') + } + } outputs.push( ...signedTransactions.map((signedTransaction) => { diff --git a/src/features/home/HomeNavigator.tsx b/src/features/home/HomeNavigator.tsx index d2c3286c8..736a7ad13 100644 --- a/src/features/home/HomeNavigator.tsx +++ b/src/features/home/HomeNavigator.tsx @@ -22,6 +22,7 @@ import SettingsNavigator from '../settings/SettingsNavigator' import SwapNavigator from '../swaps/SwapNavigator' import AddNewAccountNavigator from './addNewAccount/AddNewAccountNavigator' import ImportSubAccountsScreen from '../onboarding/import/ImportSubAccountsScreen' +import KeystoneAccountAssignScreen from '../keystone/KeystoneAccountAssignScreen' const HomeStack = createStackNavigator() @@ -49,6 +50,10 @@ const HomeStackScreen = () => { name="AccountAssignScreen" component={AccountAssignScreen} /> + { name="CLIAccountNavigator" component={CLIAccountNavigator} /> + ) } diff --git a/src/features/home/addNewAccount/AddNewAccountScreen.tsx b/src/features/home/addNewAccount/AddNewAccountScreen.tsx index c650b9954..0395e205d 100644 --- a/src/features/home/addNewAccount/AddNewAccountScreen.tsx +++ b/src/features/home/addNewAccount/AddNewAccountScreen.tsx @@ -8,6 +8,7 @@ import FadeInOut from '@components/FadeInOut' import TabBar from '@components/TabBar' import Text from '@components/Text' import globalStyles from '@theme/globalStyles' +import ConnectKeystoneStart from '../../keystone/ConnectKeystoneStartScreen' import PairStart from '../../ledger/PairStart' import AccountCreateStart from '../../onboarding/create/AccountCreateStart' import AccountImportStartScreen from '../../onboarding/import/AccountImportStartScreen' @@ -27,6 +28,7 @@ const AddNewAccountScreen = () => { { value: 'create', title: t('onboarding.create') }, { value: 'import', title: t('onboarding.import') }, { value: 'ledger', title: t('onboarding.ledger') }, + { value: 'keystone', title: t('onboarding.keystone') }, ] }, [t]) @@ -77,6 +79,11 @@ const AddNewAccountScreen = () => { )} + {selectedOption === 'keystone' && ( + + + + )} diff --git a/src/features/home/addNewAccount/addNewAccountTypes.ts b/src/features/home/addNewAccount/addNewAccountTypes.ts index f1b872202..c81c6760a 100644 --- a/src/features/home/addNewAccount/addNewAccountTypes.ts +++ b/src/features/home/addNewAccount/addNewAccountTypes.ts @@ -13,6 +13,7 @@ export type AddNewAccountParamList = { } } LedgerNavigator: undefined + KeystoneNavigator: undefined CLIAccountNavigator: undefined VoteNavigator: undefined } diff --git a/src/features/home/homeTypes.ts b/src/features/home/homeTypes.ts index 6d6a4a9c0..599746ab3 100644 --- a/src/features/home/homeTypes.ts +++ b/src/features/home/homeTypes.ts @@ -25,6 +25,7 @@ export type HomeStackParamList = { AccountManageTokenListScreen: undefined AccountTokenScreen: { mint: string } AccountAssignScreen: undefined | RouteAccount + KeystoneAccountAssignScreen: undefined ConfirmPin: { action: 'payment' } diff --git a/src/features/keystone/ConnectKeystoneStartScreen.tsx b/src/features/keystone/ConnectKeystoneStartScreen.tsx new file mode 100644 index 000000000..b0b620a32 --- /dev/null +++ b/src/features/keystone/ConnectKeystoneStartScreen.tsx @@ -0,0 +1,180 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import Box from '@components/Box' +import ButtonPressable from '@components/ButtonPressable' +import SafeAreaBox from '@components/SafeAreaBox' +import Text from '@components/Text' +import { useNavigation } from '@react-navigation/native' +import WarningKeystone from '@assets/images/warningKeystone.svg' +import React, { + forwardRef, + ReactNode, + Ref, + useCallback, + useImperativeHandle, + useMemo, + useRef, +} from 'react' +import useCamera from '@hooks/useCamera' +import { BottomSheetBackdrop, BottomSheetModal } from '@gorhom/bottom-sheet' +import { useOpacity, useSpacing } from '@theme/themeHooks' +import useBackHandler from '@hooks/useBackHandler' +import { useTheme } from '@shopify/restyle' +import { t } from 'i18next' +import { RootNavigationProp } from 'src/navigation/rootTypes' +import { Image, Linking, Platform } from 'react-native' + +type CameraPermissionBottomSheetAlertRef = { + show: () => void + dismiss: () => void +} + +const CameraPermissionBottomSheetAlert = forwardRef( + ( + { children }: { children: ReactNode }, + ref: Ref, + ) => { + useImperativeHandle(ref, () => ({ + show: () => { + bottomSheetModalRef.current?.present() + }, + dismiss: () => { + bottomSheetModalRef.current?.dismiss() + }, + })) + + const bottomSheetModalRef = useRef(null) + const { backgroundStyle } = useOpacity('surfaceSecondary', 1) + const { m } = useSpacing() + const { colors } = useTheme() + const snapPoints = useMemo(() => ['40%'], []) + const sheetHandleStyle = useMemo(() => ({ padding: m }), [m]) + const { handleDismiss } = useBackHandler(bottomSheetModalRef) + const handleIndicatorStyle = useMemo(() => { + return { + backgroundColor: colors.secondaryText, + } + }, [colors.secondaryText]) + const renderBackdrop = useCallback( + (props) => ( + + ), + [], + ) + + return ( + + {children} + + ) + }, +) + +const WarningContent = () => { + const handleOpenSettings = () => { + if (Platform.OS === 'ios') { + Linking.openURL('app-settings:') + } else { + Linking.openSettings() + } + } + return ( + + + + + + {t('keystone.connectKeystoneStart.warning') as string} + + + + + + ) +} + +const ConnectKeystoneStart = () => { + const { hasPermission } = useCamera() + const cameraPermissionBottomSheetAlertRef = + useRef(null) + const rootNav = useNavigation() + const handleStart = useCallback(() => { + if (!hasPermission) { + cameraPermissionBottomSheetAlertRef.current?.show() + } else { + rootNav.navigate('ScanQrCode') + } + }, [rootNav, hasPermission]) + return ( + + + + + + {t('keystone.connectKeystoneStart.title') as string} + + + {t('keystone.connectKeystoneStart.subtitle') as string} + + + + + + + + + ) +} + +export default React.memo(ConnectKeystoneStart) diff --git a/src/features/keystone/KeystoneAccountAssignScreen.tsx b/src/features/keystone/KeystoneAccountAssignScreen.tsx new file mode 100644 index 000000000..bca13beab --- /dev/null +++ b/src/features/keystone/KeystoneAccountAssignScreen.tsx @@ -0,0 +1,222 @@ +import AccountIcon from '@components/AccountIcon' +import Box from '@components/Box' +import CircleLoader from '@components/CircleLoader' +import FabButton from '@components/FabButton' +import SafeAreaBox from '@components/SafeAreaBox' +import Text from '@components/Text' +import TextInput from '@components/TextInput' +import CheckBox from '@react-native-community/checkbox' +import { useNavigation } from '@react-navigation/native' +import { useColors, useSpacing } from '@theme/themeHooks' +import React, { memo, useCallback, useMemo, useState } from 'react' +import { useAsyncCallback } from 'react-async-hook' +import { useTranslation } from 'react-i18next' +import { KeyboardAvoidingView, Platform, StyleSheet } from 'react-native' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import base58 from 'bs58' +import { CSAccountVersion } from '@storage/cloudStorage' +import { hex } from '@coral-xyz/anchor/dist/cjs/utils/bytes' +import { PublicKey } from '@solana/web3.js' +import Address from '@helium/address' +import { ED25519_KEY_TYPE } from '@helium/address/build/KeyTypes' +import { RootNavigationProp } from '../../navigation/rootTypes' +import { useAccountStorage } from '../../storage/AccountStorageProvider' +import { ImportAccountNavigationProp } from '../onboarding/import/importAccountNavTypes' +import { CreateAccountNavigationProp } from '../onboarding/create/createAccountNavTypes' +import { useKeystoneOnboarding } from './KeystoneOnboardingProvider' + +const KeystoneAccountAssignScreen = () => { + const onboardingNav = useNavigation< + ImportAccountNavigationProp & CreateAccountNavigationProp + >() + const rootNav = useNavigation() + const { t } = useTranslation() + const [alias, setAlias] = useState('') + const { keystoneOnboardingData } = useKeystoneOnboarding() + const insets = useSafeAreaInsets() + const spacing = useSpacing() + const colors = useColors() + const { hasAccounts, accounts } = useAccountStorage() + const [setAsDefault, toggleSetAsDefault] = useState(false) + + const existingNames = useMemo( + () => accounts && new Set(Object.values(accounts).map((a) => a.alias)), + [accounts], + ) + const accountStorage = useAccountStorage() + const { execute: handlePress, loading } = useAsyncCallback(async () => { + const getName = (index: number): string => { + const name = `${alias} ${index + 1}` + if (!existingNames?.has(name)) { + return name + } + return getName(index + 1) + } + + // convert solana public key to helium address + const solanaPublicKeyToHeliumAddress = (publicKey: string): string => { + const pkey = new PublicKey(hex.decode(publicKey)) + const heliumAddr = new Address(0, 0, ED25519_KEY_TYPE, pkey.toBytes()) + const heliumAddress = heliumAddr.b58 + return heliumAddress + } + const accountBulk = keystoneOnboardingData.accounts.map( + (account, index) => ({ + alias: getName(index), + address: solanaPublicKeyToHeliumAddress( + keystoneOnboardingData.accounts[index].publicKey, + ), + solanaAddress: base58.encode( + hex.decode(keystoneOnboardingData.accounts[index].publicKey), + ), + derivationPath: account.path, + keystoneDevice: { + masterFingerprint: account.masterFingerprint, + device: account.device, + }, + version: 'v1' as CSAccountVersion, + }), + ) + accountStorage.upsertAccounts(accountBulk) + + if (hasAccounts) { + rootNav.reset({ + index: 0, + routes: [{ name: 'TabBarNavigator' }], + }) + } else { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + onboardingNav.replace('CreateAccount', { + screen: 'AccountCreatePinScreen', + params: { + pinReset: true, + }, + }) + } + }) + + const onCheckboxToggled = useCallback( + (newValue) => toggleSetAsDefault(newValue), + [], + ) + + return ( + + + + + {t('accountAssign.title')} + + + + + + + + + + + + {t('accountAssign.setDefault')} + + + + + {!loading && existingNames?.has(alias) ? ( + + {t('accountAssign.nameExists')} + + ) : null} + {loading ? ( + + ) : ( + + )} + + + + ) +} + +const styles = StyleSheet.create({ + container: { width: '100%', flex: 1 }, +}) + +export default memo(KeystoneAccountAssignScreen) diff --git a/src/features/keystone/KeystoneModal.tsx b/src/features/keystone/KeystoneModal.tsx new file mode 100644 index 000000000..a1beed44f --- /dev/null +++ b/src/features/keystone/KeystoneModal.tsx @@ -0,0 +1,152 @@ +import Box from '@components/Box' +import { + BottomSheetBackdrop, + BottomSheetModal, + BottomSheetModalProvider, +} from '@gorhom/bottom-sheet' +import useBackHandler from '@hooks/useBackHandler' +import { useTheme } from '@shopify/restyle' +import { useOpacity, useSpacing } from '@theme/themeHooks' +import React, { + ReactNode, + Ref, + forwardRef, + memo, + useCallback, + useImperativeHandle, + useMemo, + useRef, + useState, +} from 'react' +import EventEmitter from 'events' +import { useAccountStorage } from '@storage/AccountStorageProvider' +import { KeystoneSolanaSDK } from '@keystonehq/keystone-sdk' +import { uuid } from '@keystonehq/keystone-sdk/dist/utils' +import SignTxModal from './SignTx/SignTxModal' +import { KeystoneSolSignRequest } from './types/keystoneSolanaTxType' + +export type KeystoneModalRef = { + showKeystoneModal: ({ + transaction, + }: { + transaction?: Buffer + message?: Buffer + }) => Promise +} + +let promiseResolve: (value: Buffer | PromiseLike) => void +const KeystoneModal = forwardRef( + ( + { children }: { children: ReactNode }, + ref: Ref, + ) => { + useImperativeHandle(ref, () => ({ showKeystoneModal })) + const eventEmitter = useMemo(() => new EventEmitter(), []) + const { currentAccount } = useAccountStorage() + const { backgroundStyle } = useOpacity('surfaceSecondary', 1) + const bottomSheetModalRef = useRef(null) + const [solSignRequest, setSolSignRequest] = + useState() + const showKeystoneModal = useCallback( + async ({ + transaction, + message, + }: { + transaction?: Buffer + message?: Buffer + }): Promise => { + const requestId = uuid.v4() + // why need setTimeout? modal mounted --> sleep 1s --> modal present + setTimeout(() => { + bottomSheetModalRef.current?.present() + }, 1000) + if (transaction) { + setSolSignRequest({ + requestId, + signData: transaction.toString('hex'), + dataType: KeystoneSolanaSDK.DataType.Message, + path: currentAccount?.derivationPath || '', + xfp: currentAccount?.keystoneDevice?.masterFingerprint || '', + chainId: 1, + origin: 'Helium', + }) + } + if (message) { + setSolSignRequest({ + requestId, + signData: message.toString('hex'), + dataType: KeystoneSolanaSDK.DataType.Message, + path: currentAccount?.derivationPath || '', + xfp: currentAccount?.keystoneDevice?.masterFingerprint as string, + chainId: 1, + origin: 'Helium', + }) + } + const keystonePromise = new Promise((resolve) => { + promiseResolve = resolve + }) + // listen the keystone signature event + eventEmitter.on(`keystoneSignature_${requestId}`, (signature) => { + bottomSheetModalRef.current?.dismiss() + promiseResolve(Buffer.from(signature, 'hex')) + }) + + eventEmitter.on('closeKeystoneSignatureModal', () => { + bottomSheetModalRef.current?.dismiss() + promiseResolve(Buffer.from([])) + }) + return keystonePromise + }, + [ + eventEmitter, + currentAccount?.derivationPath, + currentAccount?.keystoneDevice?.masterFingerprint, + ], + ) + const renderBackdrop = useCallback( + (props) => ( + + ), + [], + ) + const snapPoints = useMemo(() => ['100%'], []) + const { m } = useSpacing() + const { colors } = useTheme() + const sheetHandleStyle = useMemo(() => ({ padding: m }), [m]) + const { handleDismiss } = useBackHandler(bottomSheetModalRef) + + const handleIndicatorStyle = useMemo(() => { + return { + backgroundColor: colors.secondaryText, + } + }, [colors.secondaryText]) + return ( + + + + + + {children} + + + ) + }, +) + +export default memo(KeystoneModal) diff --git a/src/features/keystone/KeystoneNavigator.tsx b/src/features/keystone/KeystoneNavigator.tsx new file mode 100644 index 000000000..641bfaa74 --- /dev/null +++ b/src/features/keystone/KeystoneNavigator.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { createStackNavigator } from '@react-navigation/stack' +import ScanQrCode from './ScanQrCodeScreen' +import SelectKeystoneAccountsScreen from './SelectKeystoneAccountsScreen' +import KeystoneAccountAssignScreen from './KeystoneAccountAssignScreen' + +const KeystoneStack = createStackNavigator() + +const KeystoneNavigator = () => { + return ( + + + + + + ) +} + +export default KeystoneNavigator diff --git a/src/features/keystone/KeystoneOnboardingProvider.tsx b/src/features/keystone/KeystoneOnboardingProvider.tsx new file mode 100644 index 000000000..7404e58da --- /dev/null +++ b/src/features/keystone/KeystoneOnboardingProvider.tsx @@ -0,0 +1,58 @@ +import { NetTypes as NetType } from '@helium/address' +import React, { + createContext, + ReactNode, + useContext, + useMemo, + useState, +} from 'react' + +type KeystoneResolvedPath = { + path: string + publicKey: string + masterFingerprint: string + device: string +} + +type KeystoneOnboardingData = { + accounts: KeystoneResolvedPath[] +} + +const useKeystoneOnboardingHook = () => { + const initialState = useMemo(() => { + return { + accounts: [], + } as KeystoneOnboardingData + }, []) + const [keystoneOnboardingData, setKeystoneOnboardingData] = + useState(initialState) + + return { + keystoneOnboardingData, + setKeystoneOnboardingData, + } +} + +const initialState = { + keystoneOnboardingData: { + netType: NetType.MAINNET, + accounts: [] as KeystoneResolvedPath[], + }, + setNetType: () => undefined, + setKeystoneOnboardingData: () => undefined, +} + +const KeystoneOnboardingContext = + createContext>(initialState) + +const KeystoneOnboardingProvider = ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ) +} + +export const useKeystoneOnboarding = () => useContext(KeystoneOnboardingContext) + +export default KeystoneOnboardingProvider diff --git a/src/features/keystone/ScanQrCodeScreen.tsx b/src/features/keystone/ScanQrCodeScreen.tsx new file mode 100644 index 000000000..04926b961 --- /dev/null +++ b/src/features/keystone/ScanQrCodeScreen.tsx @@ -0,0 +1,79 @@ +import React, { useEffect, useMemo, useState } from 'react' +import DynamicQrScanner from '@components/DynamicQrScanner' +import SafeAreaBox from '@components/SafeAreaBox' +import { URDecoder } from '@ngraveio/bc-ur' +import KeystoneSDK, { MultiAccounts, UR } from '@keystonehq/keystone-sdk' +import { RootNavigationProp } from 'src/navigation/rootTypes' +import { useNavigation } from '@react-navigation/native' +import { Alert } from 'react-native' +import { useTranslation } from 'react-i18next' +import { KeystoneAccountType } from './SelectKeystoneAccountsScreen' + +const ScanQrCodeScreen = () => { + const { t } = useTranslation() + const navigation = useNavigation() + const [multiAccounts, setMultiAccounts] = useState() + const decoder = useMemo(() => new URDecoder(), []) + const [isScanQrCodeComplete, setIsScanQrCodeComplete] = useState(false) + const [progress, setProgress] = useState(0) + const [isUnexpectedQrCode, setIsUnexpectedQrCode] = useState(false) + const handleBarCodeScanned = (qrString: string) => { + // fix unexpected qrcode string + try { + decoder.receivePart(qrString.toLowerCase()) + setProgress(Number((decoder.getProgress() * 100).toFixed(0))) + if (decoder.isComplete()) { + const ur = decoder.resultUR() + const qrCodeDataRes: MultiAccounts = + new KeystoneSDK().parseMultiAccounts( + new UR(Buffer.from(ur.cbor.toString('hex'), 'hex'), ur.type), + ) + setProgress(100) + setIsScanQrCodeComplete(true) + setMultiAccounts(qrCodeDataRes) + } + } catch (error) { + setIsUnexpectedQrCode(true) + } + } + + useEffect(() => { + if (isUnexpectedQrCode) { + Alert.alert( + t('keystone.connectKeystoneStart.unexpectedQrCodeTitle'), + t('keystone.connectKeystoneStart.unexpectedQrCodeContent'), + ) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isUnexpectedQrCode]) + + useEffect(() => { + if (isScanQrCodeComplete) { + const derivationAccounts: KeystoneAccountType[] = [] + multiAccounts?.keys.forEach((key) => { + derivationAccounts.push({ + path: key.path, + publicKey: key.publicKey, + masterFingerprint: multiAccounts.masterFingerprint, + device: multiAccounts.device || 'Keystone Device', + }) + }) + setProgress(0) + setIsScanQrCodeComplete(false) + navigation.navigate('SelectKeystoneAccounts', { + derivationAccounts, + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isScanQrCodeComplete]) + return ( + + + + ) +} + +export default ScanQrCodeScreen diff --git a/src/features/keystone/SelectKeystoneAccountsScreen.tsx b/src/features/keystone/SelectKeystoneAccountsScreen.tsx new file mode 100644 index 000000000..6d125d03c --- /dev/null +++ b/src/features/keystone/SelectKeystoneAccountsScreen.tsx @@ -0,0 +1,257 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable no-restricted-syntax */ +import React, { useCallback, useEffect, useState } from 'react' +import Box from '@components/Box' +import SafeAreaBox from '@components/SafeAreaBox' +import Text from '@components/Text' +import ButtonPressable from '@components/ButtonPressable' +import { FlatList, RefreshControl } from 'react-native' +import { useColors } from '@theme/themeHooks' +import CheckBox from '@react-native-community/checkbox' +import TouchableContainer from '@components/TouchableContainer' +import { humanReadable } from '@helium/spl-utils' +import BN from 'bn.js' +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native' +import { + RootNavigationProp, + RootStackParamList, +} from 'src/navigation/rootTypes' +import { useAccountStorage } from '@storage/AccountStorageProvider' +import { ellipsizeAddress } from '@utils/accountUtils' +import base58 from 'bs58' +import { retryWithBackoff } from '@utils/retryWithBackoff' +import { PublicKey } from '@solana/web3.js' +import { useTranslation } from 'react-i18next' +import { useSolana } from '../../solana/SolanaProvider' +import { useKeystoneOnboarding } from './KeystoneOnboardingProvider' + +export type KeystoneAccountType = { + path: string + publicKey: string + masterFingerprint: string + device: string + balanceSol?: string +} + +type SelectKeystoneAccountsScreenRouteProp = RouteProp< + RootStackParamList, + 'SelectKeystoneAccounts' +> + +const SelectKeystoneAccountsScreen = () => { + const colors = useColors() + const route = useRoute() + const { setKeystoneOnboardingData } = useKeystoneOnboarding() + const { hasAccounts } = useAccountStorage() + const { derivationAccounts } = route.params + const [selected, setSelected] = React.useState>( + new Set(derivationAccounts.map((item) => item.path)), + ) + const { t } = useTranslation() + const [loading, setLoading] = useState(true) + const { connection } = useSolana() + // storage the selected accounts + const storageSelectedAccounts = () => { + const selectedAccounts: KeystoneAccountType[] = Array.from(selected).map( + (path) => { + const account = derivationAccounts.find((item) => item.path === path) + return { + path, + publicKey: account?.publicKey || '', + masterFingerprint: account?.masterFingerprint || '', + device: account?.device || '', + balanceSol: account?.balanceSol || '0', + } + }, + ) + setKeystoneOnboardingData({ + accounts: selectedAccounts, + }) + } + // next page + const navigation = useNavigation() + + const onNext = useCallback(() => { + storageSelectedAccounts() + if (hasAccounts) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + navigation.replace('TabBarNavigator', { + screen: 'Home', + params: { + screen: 'KeystoneAccountAssignScreen', + }, + }) + } else { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + navigation.replace('OnboardingNavigator', { + screen: 'KeystoneNavigator', + params: { + screen: 'KeystoneAccountAssignScreen', + }, + }) + } + }, [hasAccounts, navigation, selected]) + + const fetchBalance = async (publicKey: string) => { + if (connection) { + const balance = await connection.getBalance( + new PublicKey(base58.encode(Buffer.from(publicKey, 'hex'))), + ) + return balance.toString() + } + return '0' + } + useEffect(() => { + setLoading(true) + async function fetchBalanceForAccounts() { + for (const account of derivationAccounts) { + const balance = await retryWithBackoff(() => + fetchBalance(account.publicKey), + ) + account.balanceSol = balance + } + } + fetchBalanceForAccounts().then(() => { + setLoading(false) + }) + }, [derivationAccounts]) + + const renderItem = useCallback( + // eslint-disable-next-line react/no-unused-prop-types + ({ item, index }: { item: KeystoneAccountType; index: number }) => { + const onSelect = () => { + if (selected.has(item.path as string)) { + selected.delete(item.path as string) + setSelected(selected) + } else { + selected.add(item.path as string) + setSelected(selected) + } + } + + return ( + + + + + {item.path} + + + {ellipsizeAddress( + base58.encode(Buffer.from(item.publicKey, 'hex')), + )} + + + {item.balanceSol + ? humanReadable(new BN(item.balanceSol), 9) + : '0'}{' '} + SOL + + + + + {}} + /> + + + ) + }, + [ + colors.primaryText, + colors.secondary, + colors.transparent10, + derivationAccounts, + selected, + ], + ) + return ( + + + + {t('keystone.selectKeystoneAccounts.title')} + + + {t('keystone.selectKeystoneAccounts.subtitle')} + + {}} + title="" + tintColor={colors.primaryText} + /> + } + data={derivationAccounts} + renderItem={renderItem} + keyExtractor={(item) => item.path as string} + refreshing={loading} + onEndReached={() => {}} + /> + + + + + ) +} + +export default React.memo(SelectKeystoneAccountsScreen) diff --git a/src/features/keystone/SignTx/SignTxModal.tsx b/src/features/keystone/SignTx/SignTxModal.tsx new file mode 100644 index 000000000..6abd97d9d --- /dev/null +++ b/src/features/keystone/SignTx/SignTxModal.tsx @@ -0,0 +1,219 @@ +import SafeAreaBox from '@components/SafeAreaBox' +import React, { useEffect, useMemo, useState } from 'react' +import Keystone from '@assets/images/keystoneLogo.svg' +import Box from '@components/Box' +import Text from '@components/Text' +import ButtonPressable from '@components/ButtonPressable' +import { useTranslation } from 'react-i18next' +import KeystoneSDK, { + SolSignature, + UR, + URDecoder, +} from '@keystonehq/keystone-sdk' +import { AnimatedQrCode } from '@components/StaticQrCode' +import useAlert from '@hooks/useAlert' +import { BarcodeScanningResult, Camera, CameraView } from 'expo-camera' +import { Linking, Platform, StyleSheet } from 'react-native' +import ProgressBar from '@components/ProgressBar' +import { useAsync } from 'react-async-hook' +import EventEmitter from 'events' + +import CloseButton from '@components/CloseButton' +import { useHitSlop } from '@theme/themeHooks' +import { CameraScannerLayout } from '../../../components/CameraScannerLayout' +import { KeystoneSolSignRequest } from '../types/keystoneSolanaTxType' + +type Props = { + progress: number + onBarCodeScanned: (data: string) => void +} +const DaynamicQrScanner = ({ onBarCodeScanned, progress }: Props) => { + const [hasPermission, setHasPermission] = useState() + const { showOKCancelAlert } = useAlert() + const { t } = useTranslation() + useEffect(() => { + Camera.requestCameraPermissionsAsync().then(({ status }) => { + setHasPermission(status === 'granted') + }) + }, []) + + useAsync(async () => { + if (hasPermission !== false) return + + const decision = await showOKCancelAlert({ + title: t('qrScanner.deniedAlert.title'), + message: t('qrScanner.deniedAlert.message'), + ok: t('qrScanner.deniedAlert.ok'), + }) + + if (decision) { + if (Platform.OS === 'ios') { + Linking.openURL('app-settings:') + } else { + Linking.openSettings() + } + } + }, [hasPermission, showOKCancelAlert]) + + const handleBarCodeScanned = (result: BarcodeScanningResult) => { + onBarCodeScanned(result.data) + } + + return ( + + + + {progress > 0 && ( + + + + )} + + + + {t('keystone.payment.scanTxQrcodeScreenSubtitle3')} + + + + ) +} +const ScanTxQrcodeScreen = ({ + eventEmitter, + solSignRequest, +}: { + eventEmitter: EventEmitter + solSignRequest: KeystoneSolSignRequest +}) => { + const { t } = useTranslation() + const keystoneSDK = useMemo(() => new KeystoneSDK(), []) + const decoder = useMemo(() => new URDecoder(), []) + const solSignRequestUr = useMemo(() => { + return keystoneSDK.sol.generateSignRequest(solSignRequest) + }, [keystoneSDK, solSignRequest]) + const [openQrCodeScanner, setOpenQrCodeScanner] = useState(false) + const [progress, setProgress] = useState(0) + const [signature, setSignature] = useState(null) + const handleGetSignature = () => { + setOpenQrCodeScanner(true) + } + + const handleBarCodeScanned = (qrString: string) => { + decoder.receivePart(qrString.toLowerCase()) + setProgress(Number((decoder.getProgress() * 100).toFixed(0))) + if (decoder.isComplete()) { + const ur = decoder.resultUR() + const buffer = Buffer.from(ur.cbor.toString('hex'), 'hex') + setProgress(100) + setSignature(keystoneSDK.sol.parseSignature(new UR(buffer, ur.type))) + } + } + useEffect(() => { + if (progress === 100 && signature) { + setOpenQrCodeScanner(false) + eventEmitter.emit( + `keystoneSignature_${solSignRequest.requestId}`, + signature?.signature, + ) + setProgress(0) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [signature, progress]) + + const hitSlop = useHitSlop('l') + return ( + + {openQrCodeScanner && ( + + )} + + + {openQrCodeScanner && ( + + {t('keystone.scanQrCode')} + + )} + { + eventEmitter.emit('closeKeystoneSignatureModal', '') + }} + /> + + {!openQrCodeScanner && ( + + + + + + {t('keystone.payment.scanTxQrcodeScreenTitle')} + + + {t('keystone.payment.scanTxQrcodeScreenSubtitle1')} + + {/* show the sol sign request qrcode */} + {solSignRequestUr && ( + + )} + + {t('keystone.payment.scanTxQrcodeScreenSubtitle2')} + + + + + + )} + + ) +} + +export default React.memo(ScanTxQrcodeScreen) diff --git a/src/features/keystone/types/keystoneSolanaTxType.ts b/src/features/keystone/types/keystoneSolanaTxType.ts new file mode 100644 index 000000000..9dc465ae2 --- /dev/null +++ b/src/features/keystone/types/keystoneSolanaTxType.ts @@ -0,0 +1,9 @@ +export type KeystoneSolSignRequest = { + requestId: string + signData: string + dataType: number + path: string + xfp: string + chainId: number + origin: string +} diff --git a/src/features/migration/SolanaMigration.tsx b/src/features/migration/SolanaMigration.tsx index 5b39ef84b..df7c95944 100644 --- a/src/features/migration/SolanaMigration.tsx +++ b/src/features/migration/SolanaMigration.tsx @@ -82,15 +82,17 @@ const SolanaMigration = ({ ) const { loading, error } = useAsync(async () => { + // if current account is keystone account, then we don't need to migrate if ( !currentAccount?.solanaAddress || + currentAccount?.keystoneDevice || !anchorProvider || !cluster || (doneSolanaMigration[cluster]?.includes(currentAccount?.solanaAddress) && !manual) ) return - + // eslint-disable-next-line no-console try { await migrateWallet( anchorProvider, diff --git a/src/features/onboarding/CreateImportAccountScreen.tsx b/src/features/onboarding/CreateImportAccountScreen.tsx index bad69c894..26d3d5b96 100644 --- a/src/features/onboarding/CreateImportAccountScreen.tsx +++ b/src/features/onboarding/CreateImportAccountScreen.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next' import Plus from '@assets/images/plus.svg' import DownArrow from '@assets/images/importIcon.svg' import Ledger from '@assets/images/ledger.svg' +import Keystone from '@assets/images/keystoneLogo.svg' import { useSafeAreaInsets } from 'react-native-safe-area-context' import Box from '@components/Box' import Text from '@components/Text' @@ -33,6 +34,10 @@ const CreateImportAccountScreen = () => { navigation.navigate('LedgerNavigator') }, [navigation]) + const connectKeystone = useCallback(() => { + navigation.navigate('KeystoneNavigator') + }, [navigation]) + return ( @@ -57,6 +62,15 @@ const CreateImportAccountScreen = () => { + + + + {t('accountSetup.createImport.keystone')} + + + + + diff --git a/src/features/onboarding/OnboardingNavigator.tsx b/src/features/onboarding/OnboardingNavigator.tsx index 213252605..8859f24d4 100644 --- a/src/features/onboarding/OnboardingNavigator.tsx +++ b/src/features/onboarding/OnboardingNavigator.tsx @@ -10,6 +10,7 @@ import CreateAccountNavigator from './create/CreateAccountNavigator' import ImportAccountNavigator from './import/ImportAccountNavigator' import ImportPrivateKey from './import/ImportPrivateKey' import { OnboardingStackParamList } from './onboardingTypes' +import KeystoneNavigator from '../keystone/KeystoneNavigator' const OnboardingStack = createStackNavigator() @@ -50,6 +51,11 @@ const OnboardingNavigator = () => { component={LedgerNavigator} options={subScreenOptions} /> + -export type OnboardingOpt = 'import' | 'create' | 'ledger' +export type OnboardingOpt = 'import' | 'create' | 'ledger' | 'keystone' diff --git a/src/features/payment/PaymentCard.tsx b/src/features/payment/PaymentCard.tsx index bcf7b0539..5d3258eb9 100644 --- a/src/features/payment/PaymentCard.tsx +++ b/src/features/payment/PaymentCard.tsx @@ -43,7 +43,7 @@ const PaymentCard = ({ const { currentAccount } = useAccountStorage() const handlePayPressed = useCallback(async () => { - if (!currentAccount?.ledgerDevice) { + if (!currentAccount?.ledgerDevice && !currentAccount?.keystoneDevice) { const hasSecureAccount = await checkSecureAccount( currentAccount?.address, true, @@ -52,7 +52,11 @@ const PaymentCard = ({ } animateTransition('PaymentCard.payEnabled') setPayEnabled(true) - }, [currentAccount?.ledgerDevice, currentAccount?.address]) + }, [ + currentAccount?.ledgerDevice, + currentAccount?.address, + currentAccount?.keystoneDevice, + ]) const handleLayout = useCallback( (e: LayoutChangeEvent) => { diff --git a/src/hooks/useCamera.ts b/src/hooks/useCamera.ts new file mode 100644 index 000000000..ac3cae80d --- /dev/null +++ b/src/hooks/useCamera.ts @@ -0,0 +1,17 @@ +import { useState, useEffect } from 'react' +import { Camera } from 'expo-camera' + +const useCamera = () => { + const [hasPermission, setHasPermission] = useState(false) + + useEffect(() => { + ;(async () => { + const { status } = await Camera.requestCameraPermissionsAsync() + setHasPermission(status === 'granted') + })() + }, []) + + return { hasPermission } +} + +export default useCamera diff --git a/src/locales/en.ts b/src/locales/en.ts index 93619cf94..d84d72352 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -167,6 +167,7 @@ export default { importPrivateKey: 'Import a Private Key', ledger: 'Pair with Ledger', title: 'What would\nyou like to do?', + keystone: 'Connect Keystone to Wallet', }, createPin: { subtitle: 'Let’s secure your wallet with a PIN Code.', @@ -762,6 +763,34 @@ export default { tap: 'Get Started', title: 'Welcome to\nHelium Wallet', }, + keystone: { + connectKeystoneStart: { + subtitle: + 'Click on the "Connect with Keystone" button below to scan the QR code displayed on the Keystone device.', + title: 'Connect Keystone to Wallet', + scanQrCode: 'Scan QR Code', + warning: 'Please enable your camera permission via [Settings]', + ok: 'OK', + unexpectedQrCodeContent: + 'The QR code you scanned is not valid. Please try again.', + unexpectedQrCodeTitle: 'Unexpected QR Code', + }, + selectKeystoneAccounts: { + subtitle: + 'A secret phrase can be used to generate multiple wallets by using derivation paths. The following derivation paths have been automatically detected. Select the wallets you would like to import.', + title: 'Select Keystone Accounts', + }, + scanQrCode: 'Scan the QR Code', + payment: { + scanTxQrcodeScreenTitle: 'Scan the QR Code', + scanTxQrcodeScreenSubtitle1: 'Scan the QR code via your Keystone device', + scanTxQrcodeScreenSubtitle2: + "Click on the 'Get Signature' button after signing the transaction with your Keystone device.", + scanTxQrcodeScreenSubtitle3: + 'Place the QR code from your Keystone device in front of the camera.', + getSignature: 'Get Signature', + }, + }, ledger: { openTheSolanaApp: 'Open the Solana app on your {{ device }}', pleaseConfirmTransaction: 'Please confirm transaction on your {{ device }}', @@ -868,6 +897,7 @@ export default { create: 'New', import: 'Import', ledger: 'Ledger', + keystone: 'Keystone', }, ordinals: [ '1st', diff --git a/src/navigation/RootNavigator.tsx b/src/navigation/RootNavigator.tsx index 2f76ba21a..9ad2f2f55 100644 --- a/src/navigation/RootNavigator.tsx +++ b/src/navigation/RootNavigator.tsx @@ -18,6 +18,8 @@ import { useAsync, useAsyncCallback } from 'react-async-hook' import changeNavigationBarColor from 'react-native-navigation-bar-color' import Toast from 'react-native-simple-toast' import { useSelector } from 'react-redux' +import ScanQrCodeScreen from '../features/keystone/ScanQrCodeScreen' +import SelectKeystoneAccountsScreen from '../features/keystone/SelectKeystoneAccountsScreen' import ConnectedWallets, { ConnectedWalletsRef, } from '../features/account/ConnectedWallets' @@ -180,6 +182,18 @@ const RootNavigator = () => { component={ImportPrivateKey} options={screenOptions} /> + + ) diff --git a/src/navigation/TabBarNavigator.tsx b/src/navigation/TabBarNavigator.tsx index 43a4550cb..9ffdc6a13 100644 --- a/src/navigation/TabBarNavigator.tsx +++ b/src/navigation/TabBarNavigator.tsx @@ -169,9 +169,11 @@ const TabBarNavigator = () => { updateCluster, ]) + // keystone account do not need to migrate return ( <> {currentAccount?.solanaAddress && + !currentAccount?.keystoneDevice && anchorProvider && !doneSolanaMigration[cluster]?.includes(currentAccount.solanaAddress) && !manualMigration[cluster]?.includes(currentAccount.solanaAddress) && ( diff --git a/src/navigation/rootTypes.ts b/src/navigation/rootTypes.ts index 652d28783..79ddc408e 100644 --- a/src/navigation/rootTypes.ts +++ b/src/navigation/rootTypes.ts @@ -1,5 +1,6 @@ import { LinkWalletRequest, SignHotspotRequest } from '@helium/wallet-link' import { StackNavigationProp } from '@react-navigation/stack' +import { KeystoneAccountType } from 'src/features/keystone/SelectKeystoneAccountsScreen' import { PaymentRouteParam } from '../features/home/homeTypes' export type RootStackParamList = { @@ -11,6 +12,8 @@ export type RootStackParamList = { RequestScreen: undefined DappLoginScreen: { uri: string; callback: string } ImportPrivateKey: { key?: string } + SelectKeystoneAccounts: { derivationAccounts: KeystoneAccountType[] } + ScanQrCode: undefined } export type TabBarStackParamList = { diff --git a/src/solana/SolanaProvider.tsx b/src/solana/SolanaProvider.tsx index 5502f9c20..e296cd168 100644 --- a/src/solana/SolanaProvider.tsx +++ b/src/solana/SolanaProvider.tsx @@ -28,6 +28,9 @@ import { useAsync } from 'react-async-hook' import Config from 'react-native-config' import { useSelector } from 'react-redux' import nacl from 'tweetnacl' +import KeystoneModal, { + KeystoneModalRef, +} from '../features/keystone/KeystoneModal' import LedgerModal, { LedgerModalRef } from '../features/ledger/LedgerModal' import { useAccountStorage } from '../storage/AccountStorageProvider' import { getSessionKey, getSolanaKeypair } from '../storage/secureStorage' @@ -45,6 +48,7 @@ const useSolanaHook = () => { ) const { loading, result: sessionKey } = useAsync(getSessionKey, []) const ledgerModalRef = useRef() + const keystoneModalRef = useRef() const connection = useMemo(() => { const sessionKeyActual = !loading && !sessionKey ? Config.RPC_SESSION_KEY_FALLBACK : sessionKey @@ -69,10 +73,12 @@ const useSolanaHook = () => { const signTxn = useCallback( async (transaction: Transaction | VersionedTransaction) => { + // ledger device and keystone device will use cold wallet sign tx if ( - !currentAccount?.ledgerDevice?.id || - !currentAccount?.ledgerDevice?.type || - currentAccount?.accountIndex === undefined + (!currentAccount?.ledgerDevice?.id || + !currentAccount?.ledgerDevice?.type || + currentAccount?.accountIndex === undefined) && + !currentAccount?.keystoneDevice ) { if (!isVersionedTransaction(transaction)) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -92,6 +98,21 @@ const useSolanaHook = () => { return transaction } + if (currentAccount?.keystoneDevice) { + const signature = await keystoneModalRef.current?.showKeystoneModal({ + transaction: isVersionedTransaction(transaction) + ? Buffer.from(transaction.message.serialize()) + : transaction.serializeMessage(), + }) + if (!signature || signature.length === 0) { + throw new Error('Transaction is not signed') + } + transaction.addSignature( + new PublicKey(currentAccount.solanaAddress as string), + signature, + ) + return transaction + } const signature = await ledgerModalRef?.current?.showLedgerModal({ transaction: isVersionedTransaction(transaction) @@ -144,7 +165,8 @@ const useSolanaHook = () => { if ( (!secureAcct && !currentAccount?.ledgerDevice && - !currentAccount?.solanaAddress) || + !currentAccount?.solanaAddress && + !currentAccount?.keystoneDevice) || !connection ) return @@ -184,6 +206,7 @@ const useSolanaHook = () => { connection, currentAccount?.solanaAddress, currentAccount?.ledgerDevice, + currentAccount?.keystoneDevice, secureAcct, signTxn, ]) @@ -293,6 +316,7 @@ const useSolanaHook = () => { cache, signMsg, ledgerModalRef, + keystoneModalRef, } } @@ -305,6 +329,7 @@ const initialState: { updateCluster: (nextCluster: Cluster) => void signMsg: (msg: Buffer) => Promise ledgerModalRef: React.MutableRefObject + keystoneModalRef: React.MutableRefObject } = { anchorProvider: undefined, cluster: 'mainnet-beta' as Cluster, @@ -314,6 +339,7 @@ const initialState: { updateCluster: (_nextCluster: Cluster) => {}, signMsg: (_msg: Buffer) => Promise.resolve(_msg), ledgerModalRef: { current: undefined }, + keystoneModalRef: { current: undefined }, } const SolanaContext = createContext>(initialState) @@ -329,7 +355,11 @@ const SolanaProvider = ({ children }: { children: ReactNode }) => { connection={values.connection} cluster={values.cluster} > - {children} + + + {children} + + diff --git a/src/storage/AccountStorageProvider.tsx b/src/storage/AccountStorageProvider.tsx index 84b6c2130..7c0630fb4 100644 --- a/src/storage/AccountStorageProvider.tsx +++ b/src/storage/AccountStorageProvider.tsx @@ -271,6 +271,7 @@ const useAccountStorageHook = () => { address, netType: accountNetType(address), accountIndex: ledgerIndex ?? 0, + keystoneDevice: curr.keystoneDevice, } as CSAccount return acc }, {}) diff --git a/src/storage/cloudStorage.ts b/src/storage/cloudStorage.ts index ac770d2c3..012c58aaa 100644 --- a/src/storage/cloudStorage.ts +++ b/src/storage/cloudStorage.ts @@ -1,3 +1,6 @@ +/* eslint-disable no-debugger */ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-console */ import { NetTypes as NetType } from '@helium/address' import { heliumAddressToSolAddress } from '@helium/spl-utils' import { HELIUM_DERIVATION } from '@hooks/useDerivationAccounts' @@ -14,6 +17,11 @@ export type LedgerDevice = { type: 'usb' | 'bluetooth' } +export type KeystoneDevice = { + masterFingerprint: string + device: string +} + export type CSAccount = { alias: string address: string @@ -21,6 +29,7 @@ export type CSAccount = { netType: NetType.NetType derivationPath?: string ledgerDevice?: LedgerDevice + keystoneDevice?: KeystoneDevice accountIndex?: number // Hash of the mnemonic so we can group accts with the same mnemonic mnemonicHash?: string @@ -33,7 +42,7 @@ export type CSAccountVersion = 'v1' export type CSAccounts = Record export type CSToken = Record - +// android use sqlite as local storage and the total default sqlite size is 6 MB // for android we use AsyncStorage and auto backup to Google Drive using // https://developer.android.com/guide/topics/data/autobackup const CloudStorage = Platform.OS === 'ios' ? iCloudStorage : AsyncStorage @@ -63,18 +72,23 @@ export const sortAccounts = ( ) => { const acctList = values(accts) const sortedByAlias = sortBy(acctList, 'alias') || [] + const sortedByAliasWithSolanaAddress = sortedByAlias.map((acct) => { + return acct + }) if (defaultAddress) { - const defaultAccount = sortedByAlias.find( + const defaultAccount = sortedByAliasWithSolanaAddress.find( (a) => a.address === defaultAddress, ) if (defaultAccount) { // put default at beginning - const filtered = sortedByAlias.filter((a) => a.address !== defaultAddress) + const filtered = sortedByAliasWithSolanaAddress.filter( + (a) => a.address !== defaultAddress, + ) filtered.unshift(defaultAccount) return filtered } } - return sortedByAlias + return sortedByAliasWithSolanaAddress } const getAccounts = async (): Promise => { const csAccounts = await CloudStorage.getItem(CloudStorageKeys.ACCOUNTS) diff --git a/yarn.lock b/yarn.lock index a1aded871..031585792 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1762,6 +1762,13 @@ __metadata: languageName: node linkType: hard +"@bufbuild/protobuf@npm:^1.2.0": + version: 1.10.0 + resolution: "@bufbuild/protobuf@npm:1.10.0" + checksum: 84ba0bed65ebfc75dcb31d231af4226c87148cc4e333b59979674b187dd3e52b2a8f76431b236195d8cde5ea555bdc1ad38a76f418956f874041c15448f53402 + languageName: node + linkType: hard + "@coral-xyz/anchor@npm:^0.28.0": version: 0.28.0 resolution: "@coral-xyz/anchor@npm:0.28.0" @@ -1864,6 +1871,25 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/rlp@npm:^5.0.2": + version: 5.0.2 + resolution: "@ethereumjs/rlp@npm:5.0.2" + bin: + rlp: bin/rlp.cjs + checksum: b569061ddb1f4cf56a82f7a677c735ba37f9e94e2bbaf567404beb9e2da7aa1f595e72fc12a17c61f7aec67fd5448443efe542967c685a2fe0ffc435793dcbab + languageName: node + linkType: hard + +"@ethereumjs/util@npm:^9.0.3": + version: 9.1.0 + resolution: "@ethereumjs/util@npm:9.1.0" + dependencies: + "@ethereumjs/rlp": ^5.0.2 + ethereum-cryptography: ^2.2.1 + checksum: 594e009c3001ca1ca658b4ded01b38e72f5dd5dd76389efd90cb020de099176a3327685557df268161ac3144333cfe8abaae68cda8ae035d9cc82409d386d79a + languageName: node + linkType: hard + "@ethersproject/bytes@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/bytes@npm:5.7.0" @@ -3785,6 +3811,200 @@ __metadata: languageName: node linkType: hard +"@keystonehq/alias-sampling@npm:^0.1.1": + version: 0.1.2 + resolution: "@keystonehq/alias-sampling@npm:0.1.2" + checksum: 4dfdfb91e070b1d9f28058c92b5b8fad81696ac63bd432cd6bd359f2ab92eb50df75e8c5da1f75a351756387e9902f043b3ecc2cbf662c9c9456ecacc848abfd + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-aptos@npm:^0.6.3": + version: 0.6.3 + resolution: "@keystonehq/bc-ur-registry-aptos@npm:0.6.3" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + bs58check: ^2.1.2 + uuid: ^8.3.2 + checksum: 5be87f8aaefd038121c049fd725b3fce7867122642042299b690bce7c0b40ea98cc2d5f17c187d511e03d24d3e617ef0df70fcb1d40a5b6877710c03e3630801 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-arweave@npm:^0.5.3": + version: 0.5.3 + resolution: "@keystonehq/bc-ur-registry-arweave@npm:0.5.3" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + uuid: ^8.3.2 + checksum: 0a967f318343022dc1201561bb3cd7f5a889135bf65bb48e959153802207b0693dd9a1f6cc653eb40e0dc8e3675471df4584dc287ff7a2b7e6d6d128e38a52d4 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-btc@npm:^0.1.1": + version: 0.1.1 + resolution: "@keystonehq/bc-ur-registry-btc@npm:0.1.1" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + uuid: ^8.3.2 + checksum: d0d7ec983db55374715c04226a7fd70c82b5758c64eae8e70fb3285f8fa3e6d3f124cadb409c3371f7ae18862494ed0fa60cea4c55099eb6ba9cebda9bf2c89d + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-cardano@npm:^0.3.9": + version: 0.3.9 + resolution: "@keystonehq/bc-ur-registry-cardano@npm:0.3.9" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + uuid: ^8.3.2 + checksum: 185e9d92af543124e6ea30965ef45d51b9564e8bf88eb358a1af271d8f7b6e13fc0a81445320fbdb53f0bd8676312032d62c34a5a1bcf8fc763f3a4768b03f69 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-cosmos@npm:^0.5.3": + version: 0.5.3 + resolution: "@keystonehq/bc-ur-registry-cosmos@npm:0.5.3" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + bs58check: ^2.1.2 + uuid: ^8.3.2 + checksum: 6ca85a739cd2c15f2534735c996fbd888a42b81691cbf34c5421bde4a1bad93cd99d1ea09b6a691305d4656aa2904f592e248498e631470a17d00ed800d6a3e0 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-eth@npm:^0.20.1": + version: 0.20.1 + resolution: "@keystonehq/bc-ur-registry-eth@npm:0.20.1" + dependencies: + "@ethereumjs/util": ^9.0.3 + "@keystonehq/bc-ur-registry": ^0.6.4 + hdkey: ^2.0.1 + uuid: ^8.3.2 + checksum: 79b6312d27d1c30f4aa32f28c82ddad9acd2a47c3f3e45c816834ca8a1491284d801752fddb9883299f8f6e4b13d08e1a8e34d1fc19d028406dfc91b77ea460c + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-evm@npm:^0.5.3": + version: 0.5.3 + resolution: "@keystonehq/bc-ur-registry-evm@npm:0.5.3" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + bs58check: ^2.1.2 + uuid: ^9.0.0 + checksum: a98251b7164397edc7dcda154ebfe2adf239954bf7acb42af42162ffefec648df2384908766780ab729b8264bffc99ed4dd8de4bded74e229a281620671a326c + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-keystone@npm:^0.4.3": + version: 0.4.3 + resolution: "@keystonehq/bc-ur-registry-keystone@npm:0.4.3" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + checksum: 04848bad6fe149bebbb18113e13249acad15b1eea96cee667966f6f8ba568595d40927e46ee1202c5ea02fa475d1aff25b1c797f1ce88152804795ccb3487718 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-near@npm:^0.9.3": + version: 0.9.3 + resolution: "@keystonehq/bc-ur-registry-near@npm:0.9.3" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + bs58check: ^2.1.2 + uuid: ^8.3.2 + checksum: 494bc0842b63c701797b6cf8d06e7e980584b8efe42f9b1f3ef2d064157c4cc1b01a3c27ab74089bbfd4111c1879ede39c697ee7a11a5556aed858220878ad3d + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-sol@npm:^0.9.3": + version: 0.9.3 + resolution: "@keystonehq/bc-ur-registry-sol@npm:0.9.3" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + bs58check: ^2.1.2 + uuid: ^8.3.2 + checksum: 026409bcb1d321ccc071617dfaeeb6a6d815d97c3755273ba9581958df6536580cd6e1448614dece0f80b21d9dd9f3f8331b2195e15911856be1981fb159b413 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-stellar@npm:^0.0.4": + version: 0.0.4 + resolution: "@keystonehq/bc-ur-registry-stellar@npm:0.0.4" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + bs58check: ^2.1.2 + uuid: ^8.3.2 + checksum: 25366676d1987f05398cc7094d59020596db76c621facc1e6dc40119a3e35f231befea9911194e7c869d9177ff9704aa74033963cf90cef824c113dbffc335e5 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-sui@npm:^0.3.1": + version: 0.3.1 + resolution: "@keystonehq/bc-ur-registry-sui@npm:0.3.1" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + uuid: ^9.0.0 + checksum: 809f1c7ccb6e2017d9892e9b537b854a3e6b3d7147bf9ee8d177be7c8f4c754e48056258ab9012490544ac1994f56db2ad20b1fb83e9d5f392176eaf572da8f7 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-ton@npm:^0.1.2": + version: 0.1.2 + resolution: "@keystonehq/bc-ur-registry-ton@npm:0.1.2" + dependencies: + "@keystonehq/bc-ur-registry": ^0.6.4 + uuid: ^9.0.0 + checksum: 28458a641d02366187e9ec8f2498fc0a7ba31125b072e50ec7bfc1328dcbcbd0fc005fa29411400b5ea27ab5ba1baf53e3259516aa7346c5a2556e7deb3dc431 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry@npm:^0.6.4": + version: 0.6.4 + resolution: "@keystonehq/bc-ur-registry@npm:0.6.4" + dependencies: + "@ngraveio/bc-ur": ^1.1.5 + bs58check: ^2.1.2 + tslib: ^2.3.0 + checksum: 8b73edd304fc2c6a7faa3fae320348e9fc58493c2d75276b792ef37560534e18117c114bfb9edddd90639e81710dd660fb1a405d7c5de05e17d44613c691fdb3 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry@npm:^0.7.0": + version: 0.7.0 + resolution: "@keystonehq/bc-ur-registry@npm:0.7.0" + dependencies: + "@ngraveio/bc-ur": ^1.1.5 + bs58check: ^2.1.2 + tslib: ^2.3.0 + checksum: d6017e8fda67fc01e28aa1c047b20cce8f07b026f110a5771920879fbd658b845f529b054d1dce2fbabadcfd8da47a2160ab50c73f0bd56678aab4d83899ffcc + languageName: node + linkType: hard + +"@keystonehq/keystone-sdk@npm:^0.8.0": + version: 0.8.0 + resolution: "@keystonehq/keystone-sdk@npm:0.8.0" + dependencies: + "@bufbuild/protobuf": ^1.2.0 + "@keystonehq/bc-ur-registry": ^0.7.0 + "@keystonehq/bc-ur-registry-aptos": ^0.6.3 + "@keystonehq/bc-ur-registry-arweave": ^0.5.3 + "@keystonehq/bc-ur-registry-btc": ^0.1.1 + "@keystonehq/bc-ur-registry-cardano": ^0.3.9 + "@keystonehq/bc-ur-registry-cosmos": ^0.5.3 + "@keystonehq/bc-ur-registry-eth": ^0.20.1 + "@keystonehq/bc-ur-registry-evm": ^0.5.3 + "@keystonehq/bc-ur-registry-keystone": ^0.4.3 + "@keystonehq/bc-ur-registry-near": ^0.9.3 + "@keystonehq/bc-ur-registry-sol": ^0.9.3 + "@keystonehq/bc-ur-registry-stellar": ^0.0.4 + "@keystonehq/bc-ur-registry-sui": ^0.3.1 + "@keystonehq/bc-ur-registry-ton": ^0.1.2 + "@ngraveio/bc-ur": ^1.1.6 + bs58check: ^3.0.1 + pako: ^2.1.0 + ripple-binary-codec: ^1.4.3 + uuid: ^9.0.0 + checksum: 3a931b6f8b5fc3e162dd1475deab89fa3414a2e382acdb4373e9e81b1455192d919bb5fa94fb4ddb620f7394dea3f61bb9620e5857ca6ff5700e6f609d85365d + languageName: node + linkType: hard + "@ledgerhq/devices@npm:^8.0.7": version: 8.0.7 resolution: "@ledgerhq/devices@npm:8.0.7" @@ -4019,6 +4239,21 @@ __metadata: languageName: node linkType: hard +"@ngraveio/bc-ur@npm:^1.1.13, @ngraveio/bc-ur@npm:^1.1.5, @ngraveio/bc-ur@npm:^1.1.6": + version: 1.1.13 + resolution: "@ngraveio/bc-ur@npm:1.1.13" + dependencies: + "@keystonehq/alias-sampling": ^0.1.1 + assert: ^2.0.0 + bignumber.js: ^9.0.1 + cbor-sync: ^1.0.4 + crc: ^3.8.0 + jsbi: ^3.1.5 + sha.js: ^2.4.11 + checksum: 3f8e565c6a6dd7af7489a884f7d4d85d274ce7ce41f9fdb7e362b8a75ccbb2c934b369fd4ea58b2214d6039462ee0e933de61f372c04c551a47a75e1cad14cfd + languageName: node + linkType: hard + "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1": version: 5.1.1-v1 resolution: "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1" @@ -4028,6 +4263,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:1.4.2, @noble/curves@npm:~1.4.0": + version: 1.4.2 + resolution: "@noble/curves@npm:1.4.2" + dependencies: + "@noble/hashes": 1.4.0 + checksum: c475a83c4263e2c970eaba728895b9b5d67e0ca880651e9c6e3efdc5f6a4f07ceb5b043bf71c399fc80fada0b8706e69d0772bffdd7b9de2483b988973a34cba + languageName: node + linkType: hard + "@noble/curves@npm:^1.1.0, @noble/curves@npm:^1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" @@ -4044,7 +4288,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:^1.2.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3": +"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.2.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:~1.4.0": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" checksum: 8ba816ae26c90764b8c42493eea383716396096c5f7ba6bea559993194f49d80a73c081f315f4c367e51bd2d5891700bcdfa816b421d24ab45b41cb03e4f3342 @@ -5014,6 +5258,34 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.6": + version: 1.1.8 + resolution: "@scure/base@npm:1.1.8" + checksum: 1fc8a355ba68663c0eb430cf6a2c5ff5af790c347c1ba1953f344e8681ab37e37e2545e495f7f971b0245727d710fea8c1e57d232d0c6c543cbed4965c7596a1 + languageName: node + linkType: hard + +"@scure/bip32@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip32@npm:1.4.0" + dependencies: + "@noble/curves": ~1.4.0 + "@noble/hashes": ~1.4.0 + "@scure/base": ~1.1.6 + checksum: eff491651cbf2bea8784936de75af5fc020fc1bbb9bcb26b2cfeefbd1fb2440ebfaf30c0733ca11c0ae1e272a2ef4c3c34ba5c9fb3e1091c3285a4272045b0c6 + languageName: node + linkType: hard + +"@scure/bip39@npm:1.3.0": + version: 1.3.0 + resolution: "@scure/bip39@npm:1.3.0" + dependencies: + "@noble/hashes": ~1.4.0 + "@scure/base": ~1.1.6 + checksum: dbb0b27df753eb6c6380010b25cc9a9ea31f9cb08864fc51e69e5880ff7e2b8f85b72caea1f1f28af165e83b72c48dd38617e43fc632779d025b50ba32ea759e + languageName: node + linkType: hard + "@segment/loosely-validate-event@npm:^2.0.0": version: 2.0.0 resolution: "@segment/loosely-validate-event@npm:2.0.0" @@ -8089,6 +8361,15 @@ __metadata: languageName: node linkType: hard +"base-x@npm:^3.0.9": + version: 3.0.10 + resolution: "base-x@npm:3.0.10" + dependencies: + safe-buffer: ^5.0.1 + checksum: 52307739559e81d9980889de2359cb4f816cc0eb9a463028fa3ab239ab913d9044a1b47b4520f98e68453df32a457b8ba58b8d0ee7e757fc3fb971f3fa7a1482 + languageName: node + linkType: hard + "base-x@npm:^4.0.0": version: 4.0.0 resolution: "base-x@npm:4.0.0" @@ -8158,6 +8439,13 @@ __metadata: languageName: node linkType: hard +"big-integer@npm:^1.6.48": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 6e86885787a20fed96521958ae9086960e4e4b5e74d04f3ef7513d4d0ad631a9f3bde2730fc8aaa4b00419fc865f6ec573e5320234531ef37505da7da192c40b + languageName: node + linkType: hard + "bigint-buffer@npm:^1.1.5": version: 1.1.5 resolution: "bigint-buffer@npm:1.1.5" @@ -8460,6 +8748,27 @@ __metadata: languageName: node linkType: hard +"bs58check@npm:^2.1.2": + version: 2.1.2 + resolution: "bs58check@npm:2.1.2" + dependencies: + bs58: ^4.0.0 + create-hash: ^1.1.0 + safe-buffer: ^5.1.2 + checksum: 43bdf08a5dd04581b78f040bc4169480e17008da482ffe2a6507327bbc4fc5c28de0501f7faf22901cfe57fbca79cbb202ca529003fedb4cb8dccd265b38e54d + languageName: node + linkType: hard + +"bs58check@npm:^3.0.1": + version: 3.0.1 + resolution: "bs58check@npm:3.0.1" + dependencies: + "@noble/hashes": ^1.2.0 + bs58: ^5.0.0 + checksum: dbbecc7a09f3836e821149266c864c4bbd545539cea43c35f23f4c3c46b54c86c52b65d224b9ea2e916fa6d93bd2ce9fac5b6c6bfcf19621a9c209a5602f71c8 + languageName: node + linkType: hard + "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -8542,7 +8851,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.0.0, buffer@npm:^5.4.3, buffer@npm:^5.5.0": +"buffer@npm:^5.0.0, buffer@npm:^5.1.0, buffer@npm:^5.4.3, buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -8716,6 +9025,13 @@ __metadata: languageName: node linkType: hard +"cbor-sync@npm:^1.0.4": + version: 1.0.4 + resolution: "cbor-sync@npm:1.0.4" + checksum: 147834c64b43511b2ea601f02bc2cc4190ec8d41a7b8dc3e9037c636b484ca2124bc7d49da7a0f775ea5153ff799d57e45992816851dbb1d61335f308a0d0120 + languageName: node + linkType: hard + "chalk@npm:^2.0.1, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -9267,6 +9583,15 @@ __metadata: languageName: node linkType: hard +"crc@npm:^3.8.0": + version: 3.8.0 + resolution: "crc@npm:3.8.0" + dependencies: + buffer: ^5.1.0 + checksum: dabbc4eba223b206068b92ca82bb471d583eb6be2384a87f5c3712730cfd6ba4b13a45e8ba3ef62174d5a781a2c5ac5c20bf36cf37bba73926899bd0aa19186f + languageName: node + linkType: hard + "create-ecdh@npm:^4.0.0": version: 4.0.4 resolution: "create-ecdh@npm:4.0.4" @@ -9610,6 +9935,13 @@ __metadata: languageName: node linkType: hard +"decimal.js@npm:^10.2.0": + version: 10.4.3 + resolution: "decimal.js@npm:10.4.3" + checksum: 796404dcfa9d1dbfdc48870229d57f788b48c21c603c3f6554a1c17c10195fc1024de338b0cf9e1efe0c7c167eeb18f04548979bcc5fdfabebb7cc0ae3287bae + languageName: node + linkType: hard + "decode-uri-component@npm:^0.2.0, decode-uri-component@npm:^0.2.2": version: 0.2.2 resolution: "decode-uri-component@npm:0.2.2" @@ -10093,6 +10425,21 @@ __metadata: languageName: node linkType: hard +"elliptic@npm:^6.5.4": + version: 6.5.7 + resolution: "elliptic@npm:6.5.7" + dependencies: + bn.js: ^4.11.9 + brorand: ^1.1.0 + hash.js: ^1.0.0 + hmac-drbg: ^1.0.1 + inherits: ^2.0.4 + minimalistic-assert: ^1.0.1 + minimalistic-crypto-utils: ^1.0.1 + checksum: af0ffddffdbc2fea4eeec74388cd73e62ed5a0eac6711568fb28071566319785df529c968b0bf1250ba4bc628e074b2d64c54a633e034aa6f0c6b152ceb49ab8 + languageName: node + linkType: hard + "eme-encryption-scheme-polyfill@npm:^2.0.1": version: 2.1.1 resolution: "eme-encryption-scheme-polyfill@npm:2.1.1" @@ -10927,6 +11274,18 @@ __metadata: languageName: node linkType: hard +"ethereum-cryptography@npm:^2.2.1": + version: 2.2.1 + resolution: "ethereum-cryptography@npm:2.2.1" + dependencies: + "@noble/curves": 1.4.2 + "@noble/hashes": 1.4.0 + "@scure/bip32": 1.4.0 + "@scure/bip39": 1.3.0 + checksum: 1466e4c417b315a6ac67f95088b769fafac8902b495aada3c6375d827e5a7882f9e0eea5f5451600d2250283d9198b8a3d4d996e374e07a80a324e29136f25c6 + languageName: node + linkType: hard + "event-target-shim@npm:^5.0.0, event-target-shim@npm:^5.0.1": version: 5.0.1 resolution: "event-target-shim@npm:5.0.1" @@ -12324,6 +12683,18 @@ __metadata: languageName: node linkType: hard +"hdkey@npm:^2.0.1": + version: 2.1.0 + resolution: "hdkey@npm:2.1.0" + dependencies: + bs58check: ^2.1.2 + ripemd160: ^2.0.2 + safe-buffer: ^5.1.1 + secp256k1: ^4.0.0 + checksum: 042f2d715dc4d106c868dc3791d584336845e4e53f3452e1df116d6af5d88d7084a0a73ddd8a07b4a7d9e6b29cd3b6b4174f03499f25d8ddd101642b34fabe5c + languageName: node + linkType: hard + "helium-wallet@workspace:.": version: 0.0.0-use.local resolution: "helium-wallet@workspace:." @@ -12366,6 +12737,7 @@ __metadata: "@helium/voter-stake-registry-sdk": 0.9.7 "@helium/wallet-link": 4.11.0 "@jup-ag/api": ^6.0.6 + "@keystonehq/keystone-sdk": ^0.8.0 "@ledgerhq/hw-app-solana": 7.0.13 "@ledgerhq/hw-transport-mocker": 6.27.2 "@ledgerhq/react-native-hid": 6.30.0 @@ -12374,6 +12746,7 @@ __metadata: "@maplibre/maplibre-react-native": ^9.1.0 "@metaplex-foundation/mpl-bubblegum": 0.6.0 "@metaplex-foundation/mpl-token-metadata": 2.10.0 + "@ngraveio/bc-ur": ^1.1.13 "@onsol/tldparser": ^0.5.3 "@react-native-async-storage/async-storage": 1.18.1 "@react-native-community/blur": 4.3.0 @@ -14230,6 +14603,13 @@ __metadata: languageName: node linkType: hard +"jsbi@npm:^3.1.5": + version: 3.2.5 + resolution: "jsbi@npm:3.2.5" + checksum: 642d1bb139ad1c1e96c4907eb159565e980a0d168487626b493d0d0b7b341da0e43001089d3b21703fe17b18a7a6c0f42c92026f71d54471ed0a0d1b3015ec0f + languageName: node + linkType: hard + "jsbn@npm:1.1.0": version: 1.1.0 resolution: "jsbn@npm:1.1.0" @@ -16010,6 +16390,15 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^2.0.0": + version: 2.0.2 + resolution: "node-addon-api@npm:2.0.2" + dependencies: + node-gyp: latest + checksum: 31fb22d674648204f8dd94167eb5aac896c841b84a9210d614bf5d97c74ef059cc6326389cf0c54d2086e35312938401d4cc82e5fcd679202503eb8ac84814f8 + languageName: node + linkType: hard + "node-dir@npm:^0.1.17": version: 0.1.17 resolution: "node-dir@npm:0.1.17" @@ -16050,6 +16439,17 @@ __metadata: languageName: node linkType: hard +"node-gyp-build@npm:^4.2.0": + version: 4.8.2 + resolution: "node-gyp-build@npm:4.8.2" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 1a57bba8c4c193f808bd8ad1484d4ebdd8106dd9f04a3e82554dc716e3a2d87d7e369e9503c145e0e6a7e2c663fec0d8aaf52bd8156342ec7fc388195f37824e + languageName: node + linkType: hard + "node-gyp-build@npm:^4.3.0": version: 4.6.1 resolution: "node-gyp-build@npm:4.6.1" @@ -16583,7 +16983,7 @@ __metadata: languageName: node linkType: hard -"pako@npm:^2.0.3": +"pako@npm:^2.0.3, pako@npm:^2.1.0": version: 2.1.0 resolution: "pako@npm:2.1.0" checksum: 71666548644c9a4d056bcaba849ca6fd7242c6cf1af0646d3346f3079a1c7f4a66ffec6f7369ee0dc88f61926c10d6ab05da3e1fca44b83551839e89edd75a3e @@ -18945,7 +19345,7 @@ __metadata: languageName: node linkType: hard -"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": +"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1, ripemd160@npm:^2.0.2": version: 2.0.2 resolution: "ripemd160@npm:2.0.2" dependencies: @@ -18955,6 +19355,30 @@ __metadata: languageName: node linkType: hard +"ripple-address-codec@npm:^4.3.1": + version: 4.3.1 + resolution: "ripple-address-codec@npm:4.3.1" + dependencies: + base-x: ^3.0.9 + create-hash: ^1.1.2 + checksum: 2961fa9ffd508137a8fbf52cc75cd34e76245f515d0f0595f3abb3a29a8df0014518c816d2db45fd6dbab433595f345a048781753fedfddeeb4a47f2d5e9c39e + languageName: node + linkType: hard + +"ripple-binary-codec@npm:^1.4.3": + version: 1.11.0 + resolution: "ripple-binary-codec@npm:1.11.0" + dependencies: + assert: ^2.0.0 + big-integer: ^1.6.48 + buffer: 6.0.3 + create-hash: ^1.2.0 + decimal.js: ^10.2.0 + ripple-address-codec: ^4.3.1 + checksum: 901f6da22bb31860e8c149974c55c72ba5a7d50d635b7efa9be81ce35cea6576a3b0c59b480069141829d73c558721ab17f34df801d4d68af8f3ae4ed0bbd42c + languageName: node + linkType: hard + "rn-host-detect@npm:1.2.0": version: 1.2.0 resolution: "rn-host-detect@npm:1.2.0" @@ -19153,6 +19577,18 @@ __metadata: languageName: node linkType: hard +"secp256k1@npm:^4.0.0": + version: 4.0.3 + resolution: "secp256k1@npm:4.0.3" + dependencies: + elliptic: ^6.5.4 + node-addon-api: ^2.0.0 + node-gyp: latest + node-gyp-build: ^4.2.0 + checksum: 21e219adc0024fbd75021001358780a3cc6ac21273c3fcaef46943af73969729709b03f1df7c012a0baab0830fb9a06ccc6b42f8d50050c665cb98078eab477b + languageName: node + linkType: hard + "secure-json-parse@npm:^2.5.0": version: 2.7.0 resolution: "secure-json-parse@npm:2.7.0" @@ -19373,7 +19809,7 @@ __metadata: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8": version: 2.4.11 resolution: "sha.js@npm:2.4.11" dependencies: @@ -20641,6 +21077,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.3.0": + version: 2.7.0 + resolution: "tslib@npm:2.7.0" + checksum: 1606d5c89f88d466889def78653f3aab0f88692e80bb2066d090ca6112ae250ec1cfa9dbfaab0d17b60da15a4186e8ec4d893801c67896b277c17374e36e1d28 + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0"