diff --git a/ios/Podfile b/ios/Podfile index fe272ac77..639bb19d3 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,11 +1,13 @@ require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' +require_relative '../node_modules/react-native-permissions/scripts/setup' platform :ios, '13.0' prepare_react_native_project! $RNMapboxMapsImpl = 'mapbox' + # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded # @@ -76,6 +78,10 @@ target 'HeliumWallet' do $RNMapboxMaps.pre_install(installer) end + + permissions_path = '../node_modules/react-native-permissions/ios' + setup_permissions(['BluetoothPeripheral']) + post_install do |installer| $RNMapboxMaps.post_install(installer) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b761b4cf7..b8ea5597f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -59,7 +59,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.71.5) - fmt (6.2.1) - glog (0.3.5) - - helium-react-native-sdk (1.0.0): + - helium-react-native-sdk (2.1.4): - React-Core - hermes-engine (0.71.5): - hermes-engine/Pre-built (= 0.71.5) @@ -499,6 +499,8 @@ PODS: - Turf - RNOS (1.2.6): - React + - RNPermissions (3.9.2): + - React-Core - RNReactNativeSharedGroupPreferences (1.1.23): - React - RNReanimated (2.14.4): @@ -633,6 +635,7 @@ DEPENDENCIES: - RNLocalize (from `../node_modules/react-native-localize`) - "rnmapbox-maps (from `../node_modules/@rnmapbox/maps`)" - RNOS (from `../node_modules/react-native-os`) + - RNPermissions (from `../node_modules/react-native-permissions`) - RNReactNativeSharedGroupPreferences (from `../node_modules/react-native-shared-group-preferences`) - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) @@ -818,6 +821,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@rnmapbox/maps" RNOS: :path: "../node_modules/react-native-os" + RNPermissions: + :path: "../node_modules/react-native-permissions" RNReactNativeSharedGroupPreferences: :path: "../node_modules/react-native-shared-group-preferences" RNReanimated: @@ -862,7 +867,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 627fd07f1b9d498c9fa572e76d7f1a6b1ee9a444 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 791fe035093b84822da7f0870421a25839ca7870 - helium-react-native-sdk: 32c0a7e3abc733a7f3d291013b2db31475fc6980 + helium-react-native-sdk: 816b3d02192e23ccb4d0be21780ea7c5ed46de4e hermes-engine: 7a53ccac09146018a08239c5425625fdb79a6162 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 MapboxCommon: fdf7fd31c90b7b607cd9c63e37797f023c01d860 @@ -925,6 +930,7 @@ SPEC CHECKSUMS: RNLocalize: a64514b46a01375fdfae9349036b4dc7130333b5 rnmapbox-maps: 3553325eec9e1501942bbb28702b3a44457961d2 RNOS: 6f2f9a70895bbbfbdad7196abd952e7b01d45027 + RNPermissions: 2af759cf053542b2b4b3c4cf9f43874796106f2c RNReactNativeSharedGroupPreferences: de0121a4224c267bc7e9fb16c398f3f087c8da81 RNReanimated: cc5e3aa479cb9170bcccf8204291a6950a3be128 RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f @@ -939,6 +945,6 @@ SPEC CHECKSUMS: Yoga: cd7d7f509dbfac14ee7f31a6c750acb957cd5022 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 315053ef8dad0ea72bc8b3d40f59c1020ede650c +PODFILE CHECKSUM: 5b4d3c6d9c5a303c84f0b5427958ba4a25671e76 COCOAPODS: 1.11.3 diff --git a/package.json b/package.json index a157a3de4..e41f10d59 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@helium/lazy-distributor-sdk": "^0.3.2", "@helium/onboarding": "4.9.0", "@helium/proto-ble": "4.0.0", - "@helium/react-native-sdk": "1.0.0", + "@helium/react-native-sdk": "^2.1.4", "@helium/spl-utils": "^0.3.2", "@helium/transactions": "4.8.1", "@helium/treasury-management-sdk": "0.1.2", @@ -167,6 +167,7 @@ "react-native-onesignal": "4.4.1", "react-native-os": "1.2.6", "react-native-pager-view": "6.1.2", + "react-native-permissions": "^3.9.0", "react-native-qrcode-svg": "6.1.2", "react-native-randombytes": "3.6.1", "react-native-reanimated": "2.14.4", diff --git a/src/App.tsx b/src/App.tsx index d49213172..0afc6512e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ import { BottomSheetModalProvider } from '@gorhom/bottom-sheet' import { PortalHost, PortalProvider } from '@gorhom/portal' import { AccountContext } from '@helium/account-fetch-cache-hooks' +import { OnboardingProvider } from '@helium/react-native-sdk' import { DarkTheme, NavigationContainer } from '@react-navigation/native' import MapboxGL from '@rnmapbox/maps' import { ThemeProvider } from '@shopify/restyle' @@ -21,7 +22,6 @@ import NetworkAwareStatusBar from './components/NetworkAwareStatusBar' import SplashScreen from './components/SplashScreen' import WalletConnectProvider from './features/dappLogin/WalletConnectProvider' import LockScreen from './features/lock/LockScreen' -import OnboardingProvider from './features/onboarding/OnboardingProvider' import SecurityScreen from './features/security/SecurityScreen' import useMount from './hooks/useMount' import { navigationRef } from './navigation/NavigationHelper' diff --git a/src/assets/images/bluetooth.svg b/src/assets/images/bluetooth.svg new file mode 100644 index 000000000..43e690a02 --- /dev/null +++ b/src/assets/images/bluetooth.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/images/collectableIcon.svg b/src/assets/images/collectableIcon.svg new file mode 100644 index 000000000..092e0bfbe --- /dev/null +++ b/src/assets/images/collectableIcon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/gem.svg b/src/assets/images/gem.svg deleted file mode 100644 index cef27ac9d..000000000 --- a/src/assets/images/gem.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/index.tsx b/src/components/index.tsx new file mode 100644 index 000000000..f5209bd38 --- /dev/null +++ b/src/components/index.tsx @@ -0,0 +1,30 @@ +import { ReAnimatedBlurBox, ReAnimatedBox } from '@components/AnimatedBox' +import BackScreen from '@components/BackScreen' +import Box from '@components/Box' +import ButtonPressable from '@components/ButtonPressable' +import CircleLoader from '@components/CircleLoader' +import FabButton from '@components/FabButton' +import FadeInOut, { DelayedFadeIn, FadeInFast } from '@components/FadeInOut' +import ImageBox from '@components/ImageBox' +import SafeAreaBox from '@components/SafeAreaBox' +import SearchInput from '@components/SearchInput' +import Text from '@components/Text' +import TextInput from '@components/TextInput' + +export { + ReAnimatedBlurBox, + ReAnimatedBox, + BackScreen, + Box, + ButtonPressable, + CircleLoader, + FabButton, + FadeInOut, + DelayedFadeIn, + FadeInFast, + ImageBox, + SafeAreaBox, + SearchInput, + Text, + TextInput, +} diff --git a/src/features/collectables/AssertLocationScreen.tsx b/src/features/collectables/AssertLocationScreen.tsx index a0ab3ce7e..e8157c32a 100644 --- a/src/features/collectables/AssertLocationScreen.tsx +++ b/src/features/collectables/AssertLocationScreen.tsx @@ -1,55 +1,66 @@ import MapPin from '@assets/images/mapPin.svg' -import { ReAnimatedBlurBox, ReAnimatedBox } from '@components/AnimatedBox' -import BackScreen from '@components/BackScreen' -import Box from '@components/Box' -import ButtonPressable from '@components/ButtonPressable' -import CircleLoader from '@components/CircleLoader' -import FabButton from '@components/FabButton' -import { DelayedFadeIn, FadeInFast } from '@components/FadeInOut' -import ImageBox from '@components/ImageBox' -import SafeAreaBox from '@components/SafeAreaBox' -import SearchInput from '@components/SearchInput' -import Text from '@components/Text' -import TextInput from '@components/TextInput' +import { + BackScreen, + Box, + ButtonPressable, + CircleLoader, + DelayedFadeIn, + FabButton, + FadeInFast, + FadeInOut, + ImageBox, + ReAnimatedBlurBox, + ReAnimatedBox, + SafeAreaBox, + SearchInput, + Text, + TextInput, +} from '@components' +import TouchableOpacityBox from '@components/TouchableOpacityBox' import { HotspotType } from '@helium/onboarding' import useAlert from '@hooks/useAlert' +import { useEntityKey } from '@hooks/useEntityKey' import { useForwardGeo } from '@hooks/useForwardGeo' +import { useIotInfo } from '@hooks/useIotInfo' +import { useMobileInfo } from '@hooks/useMobileInfo' import { useReverseGeo } from '@hooks/useReverseGeo' import useSubmitTxn from '@hooks/useSubmitTxn' -import { RouteProp, useRoute } from '@react-navigation/native' +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native' import MapboxGL from '@rnmapbox/maps' import turfBbox from '@turf/bbox' import { points } from '@turf/helpers' +import { parseH3BNLocation } from '@utils/h3' +import { removeDashAndCapitalize } from '@utils/hotspotNftsUtils' +import * as Logger from '@utils/logger' +import { MAX_MAP_ZOOM, MIN_MAP_ZOOM } from '@utils/mapbox' import debounce from 'lodash/debounce' import React, { memo, useCallback, useEffect, useMemo, - useState, useRef, + useState, } from 'react' import { useTranslation } from 'react-i18next' import { Alert, - KeyboardAvoidingView, Keyboard, + KeyboardAvoidingView, TouchableWithoutFeedback, } from 'react-native' import { Config } from 'react-native-config' import { Edge } from 'react-native-safe-area-context' import 'text-encoding-polyfill' -import { useEntityKey } from '@hooks/useEntityKey' -import { useIotInfo } from '@hooks/useIotInfo' -import { useMobileInfo } from '@hooks/useMobileInfo' -import { parseH3BNLocation } from '../../utils/h3' -import { removeDashAndCapitalize } from '../../utils/hotspotNftsUtils' -import * as Logger from '../../utils/logger' -import { MAX_MAP_ZOOM, MIN_MAP_ZOOM } from '../../utils/mapbox' -import { CollectableStackParamList } from './collectablesTypes' +import { useDebounce } from 'use-debounce' +import { + CollectableNavigationProp, + CollectableStackParamList, +} from './collectablesTypes' const BUTTON_HEIGHT = 65 type Route = RouteProp + const AssertLocationScreen = () => { const { t } = useTranslation() const route = useRoute() @@ -74,6 +85,7 @@ const AssertLocationScreen = () => { const reverseGeo = useReverseGeo(mapCenter) const forwardGeo = useForwardGeo() const { submitUpdateEntityInfo } = useSubmitTxn() + const collectNav = useNavigation() const { content: { metadata }, @@ -237,37 +249,44 @@ const AssertLocationScreen = () => { const assertLocation = useCallback( async (type: HotspotType) => { - if (mapCenter && entityKey) { - setTransactionError(undefined) - setAsserting(true) - try { - hideElevGain() - await submitUpdateEntityInfo({ - type, - entityKey, - lng: mapCenter[0], - lat: mapCenter[1], - elevation, - decimalGain: gain, - }) - setAsserting(false) - } catch (error) { - setAsserting(false) - Logger.error(error) - setTransactionError((error as Error).message) - } + if (!mapCenter || !entityKey) return + + setTransactionError(undefined) + setAsserting(true) + try { + hideElevGain() + await submitUpdateEntityInfo({ + type, + entityKey, + lng: mapCenter[0], + lat: mapCenter[1], + elevation, + decimalGain: gain, + }) + setAsserting(false) + + await showOKAlert({ + title: t('assertLocationScreen.success.title'), + message: t('assertLocationScreen.success.message'), + }) + collectNav.navigate('HotspotDetailsScreen', { collectable }) + } catch (error) { + setAsserting(false) + Logger.error(error) + setTransactionError((error as Error).message) } }, [ - entityKey, mapCenter, - elevation, - gain, + entityKey, hideElevGain, - setAsserting, - setTransactionError, submitUpdateEntityInfo, - // nav, + elevation, + gain, + showOKAlert, + t, + collectNav, + collectable, ], ) @@ -302,13 +321,20 @@ const AssertLocationScreen = () => { if (transactionError) return transactionError }, [transactionError]) + const disabled = useMemo( + () => !mapCenter || reverseGeo.loading || asserting, + [asserting, mapCenter, reverseGeo.loading], + ) + const [debouncedDisabled] = useDebounce(disabled, 300) + const [reverseGeoLoading] = useDebounce(reverseGeo.loading, 300) + return ( { marginVertical="s" minHeight={40} > - {reverseGeo.loading && ( + {reverseGeoLoading && ( )} {showError && ( @@ -533,35 +559,36 @@ const AssertLocationScreen = () => { )} {!reverseGeo.loading && !showError && ( - - {reverseGeo.result} - + + + {reverseGeo.result} + + )} - - ) : undefined - } - /> + paddingVertical="lm" + disabled={disabled} + height={65} + alignItems="center" + justifyContent="center" + onPress={handleAssertLocationPress} + > + {debouncedDisabled || asserting ? ( + + ) : ( + + {t('assertLocationScreen.title')} + + )} + diff --git a/src/features/collectables/CollectablesNavigator.tsx b/src/features/collectables/CollectablesNavigator.tsx index 104becc5c..9d2062e42 100644 --- a/src/features/collectables/CollectablesNavigator.tsx +++ b/src/features/collectables/CollectablesNavigator.tsx @@ -21,6 +21,7 @@ import CollectionScreen from './CollectionScreen' import NftDetailsScreen from './NftDetailsScreen' import AntennaSetupScreen from './AntennaSetupScreen' import SettingUpAntennaScreen from './SettingUpAntennaScreen' +import OnboardingNav from '../hotspot-onboarding/OnboardingNav' const CollectablesStack = createStackNavigator() @@ -113,6 +114,12 @@ const CollectablesStackScreen = () => { name="TransferCompleteScreen" component={TransferCompleteScreen} /> + ) } diff --git a/src/features/collectables/HotspotList.tsx b/src/features/collectables/HotspotList.tsx index a08c9c5ce..f5f027bda 100644 --- a/src/features/collectables/HotspotList.tsx +++ b/src/features/collectables/HotspotList.tsx @@ -149,6 +149,10 @@ const HotspotList = () => { navigation.navigate('ClaimAllRewardsScreen') }, [navigation]) + const handleNavigateToHotspotOnboard = useCallback(() => { + navigation.navigate('OnboardingNavigator') + }, [navigation]) + const toggleFiltersOpen = useCallback( (open) => () => { setFiltersOpen(open) @@ -267,7 +271,6 @@ const HotspotList = () => { titleColorDisabled="secondaryText" title={t('collectablesScreen.hotspots.claimAllRewards')} titleColor="black" - marginBottom="m" disabled={ (pendingIotRewards && pendingIotRewards.eq(new BN('0')) && @@ -277,6 +280,20 @@ const HotspotList = () => { } onPress={handleNavigateToClaimRewards} /> + ) }, [ @@ -290,6 +307,7 @@ const HotspotList = () => { toggleFiltersOpen, hotspotsWithMeta, pageAmount, + handleNavigateToHotspotOnboard, ]) const renderCollectable = useCallback( diff --git a/src/features/collectables/collectablesTypes.ts b/src/features/collectables/collectablesTypes.ts index 0b4e65a8b..8b8e176a1 100644 --- a/src/features/collectables/collectablesTypes.ts +++ b/src/features/collectables/collectablesTypes.ts @@ -47,6 +47,7 @@ export type CollectableStackParamList = { PaymentQrScanner: undefined AddressBookNavigator: undefined ScanAddress: undefined + OnboardingNavigator: undefined } export type CollectableNavigationProp = diff --git a/src/features/hotspot-onboarding/OnboardingNav.tsx b/src/features/hotspot-onboarding/OnboardingNav.tsx new file mode 100644 index 000000000..d11673be8 --- /dev/null +++ b/src/features/hotspot-onboarding/OnboardingNav.tsx @@ -0,0 +1,31 @@ +import { HotspotBleProvider } from '@helium/react-native-sdk' +import { + createNativeStackNavigator, + NativeStackNavigationOptions, +} from '@react-navigation/native-stack' +import * as React from 'react' +import HotspotBLENav from './iot-ble/HotspotBLENav' +import SelectDevice from './SelectDevice' + +const Stack = createNativeStackNavigator() + +const screenOptions = { headerShown: false } as NativeStackNavigationOptions + +export default React.memo(function OnboardingNav() { + return ( + + + + + + + ) +}) diff --git a/src/features/hotspot-onboarding/SelectDevice.tsx b/src/features/hotspot-onboarding/SelectDevice.tsx new file mode 100644 index 000000000..f79ff4a13 --- /dev/null +++ b/src/features/hotspot-onboarding/SelectDevice.tsx @@ -0,0 +1,87 @@ +import BackScreen from '@components/BackScreen' +import Bluetooth from '@assets/images/bluetooth.svg' +import ImageBox from '@components/ImageBox' +import Text from '@components/Text' +import TouchableOpacityBox from '@components/TouchableOpacityBox' +import { useNavigation } from '@react-navigation/native' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { FlatList } from 'react-native' +import { OnboardableDevice, OnboardingNavProp } from './navTypes' + +const data: OnboardableDevice[] = [ + { + name: 'Bluetooth Enabled Hotspot', + type: 'IotBle', + icon: , + options: { + bleInstructions: + 'Power on your Hotspot. Follow manufacturer instructions for enabling bluetooth discovery on the Hotspot.', + }, + }, +] + +const SelectOnboardableDevice = () => { + const { t } = useTranslation() + const navigation = useNavigation() + const renderItem = React.useCallback( + // eslint-disable-next-line react/no-unused-prop-types + ({ item, index }: { item: OnboardableDevice; index: number }) => { + return ( + { + navigation.push(item.type, item.options) + }} + alignItems="center" + padding="s" + flexDirection="row" + borderTopWidth={index === 0 ? 0 : 1} + borderColor="grey900" + borderBottomWidth={1} + > + {item.image && ( + + )} + {item.icon && item.icon} + + {item.name} + + + ) + }, + [navigation], + ) + + const keyExtractor = React.useCallback( + ({ name }: OnboardableDevice) => name, + [], + ) + + return ( + + + {t('hotspotOnboarding.selectOnboardingMethod.subtitle')} + + + + ) +} + +export default SelectOnboardableDevice diff --git a/src/features/hotspot-onboarding/iot-ble/AddGatewayBle.tsx b/src/features/hotspot-onboarding/iot-ble/AddGatewayBle.tsx new file mode 100644 index 000000000..c7e5cfe1b --- /dev/null +++ b/src/features/hotspot-onboarding/iot-ble/AddGatewayBle.tsx @@ -0,0 +1,238 @@ +import BackScreen from '@components/BackScreen' +import ButtonPressable from '@components/ButtonPressable' +import CircleLoader from '@components/CircleLoader' +import Text from '@components/Text' +import { + init, + iotInfoKey, + keyToAssetKey, + rewardableEntityConfigKey, +} from '@helium/helium-entity-manager-sdk' +import { + AddGatewayV1, + useHotspotBle, + useOnboarding, +} from '@helium/react-native-sdk' +import { + bufferToTransaction, + heliumAddressToSolAddress, + sendAndConfirmWithRetry, +} from '@helium/spl-utils' +import { useNavigation } from '@react-navigation/native' +import { LAMPORTS_PER_SOL, PublicKey, Transaction } from '@solana/web3.js' +import { useAccountStorage } from '@storage/AccountStorageProvider' +import { DAO_KEY, IOT_SUB_DAO_KEY } from '@utils/constants' +import { getHotspotWithRewards } from '@utils/solanaUtils' +import { Buffer } from 'buffer' +import React from 'react' +import { useAsyncCallback } from 'react-async-hook' +import { useTranslation } from 'react-i18next' +import { Alert } from 'react-native' +import { TabBarNavigationProp } from '../../../navigation/rootTypes' +import { useSolana } from '../../../solana/SolanaProvider' +import { CollectableNavigationProp } from '../../collectables/collectablesTypes' + +const AddGatewayBle = () => { + const { getOnboardingRecord, getOnboardTransactions, onboardingClient } = + useOnboarding() + const { createGatewayTxn, getOnboardingAddress } = useHotspotBle() + const { currentAccount } = useAccountStorage() + const { anchorProvider } = useSolana() + const { t } = useTranslation() + const tabNav = useNavigation() + const collectNav = useNavigation() + + const { + execute: handleAddGateway, + loading, + error, + } = useAsyncCallback(async () => { + if (!anchorProvider) { + Alert.alert('Error', 'No anchor provider') + return + } + const accountAddress = currentAccount?.address + if (!accountAddress) { + Alert.alert( + 'Error', + 'You must first add a wallet address from the main menu', + ) + return + } + + const onboardAddress = await getOnboardingAddress() + const onboardRecord = await getOnboardingRecord(onboardAddress) + + if (!onboardRecord) { + throw new Error( + t('hotspotOnboarding.onboarding.hotspotNotFound', { + onboardAddress, + }), + ) + } + + if (!onboardRecord?.maker.address) { + throw new Error(t('hotspotOnboarding.onboarding.makerNotFound')) + } + const makerSolAddr = heliumAddressToSolAddress(onboardRecord?.maker.address) + const makerSolBalance = ( + await anchorProvider.connection.getAccountInfo( + new PublicKey(makerSolAddr), + ) + )?.lamports + if ( + !makerSolBalance || + makerSolBalance / LAMPORTS_PER_SOL < 0.00089088 + 0.00001 + ) { + throw new Error( + t('hotspotOnboarding.onboarding.manufacturerMissingSol', { + name: onboardRecord?.maker.name, + }), + ) + } + + const txnStr = await createGatewayTxn({ + ownerAddress: accountAddress, + payerAddress: onboardRecord?.maker.address, + }) + const tx = AddGatewayV1.fromString(txnStr) + if (!tx?.gateway?.b58) { + throw new Error('Error signing gateway txn') + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function wrapProgramError(e: any) { + if ( + e.toString().includes('Insufficient Balance') || + e.toString().includes('"Custom":1') || + e.InstructionError[1].Custom === 1 + ) { + throw new Error( + t('hotspotOnboarding.onboarding.manufacturerMissingDcOrSol', { + name: onboardRecord?.maker.name, + }), + ) + } + if (e.InstructionError) { + throw new Error(`Program Error: ${JSON.stringify(e)}`) + } + throw e + } + + const createTxns = await onboardingClient.createHotspot({ + transaction: txnStr, + }) + + const createHotspotTxns = createTxns.data?.solanaTransactions?.map( + (createHotspotTx) => Buffer.from(createHotspotTx), + ) + try { + // eslint-disable-next-line no-restricted-syntax + for (const txn of createHotspotTxns || []) { + // eslint-disable-next-line no-await-in-loop + await sendAndConfirmWithRetry( + anchorProvider.connection, + txn, + { + skipPreflight: true, + }, + 'confirmed', + ) + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + wrapProgramError(e) + } + + const hemProgram = await init(anchorProvider) + const [iotConfigKey] = rewardableEntityConfigKey(IOT_SUB_DAO_KEY, 'IOT') + const iotInfo = await hemProgram.account.iotHotspotInfoV0.fetchNullable( + iotInfoKey(iotConfigKey, tx?.gateway?.b58)[0], + ) + if (!iotInfo) { + const { solanaTransactions } = await getOnboardTransactions({ + hotspotAddress: onboardAddress, + hotspotTypes: ['iot'], + }) + let solanaSignedTransactions: Transaction[] | undefined + + if (solanaTransactions) { + solanaSignedTransactions = + await anchorProvider?.wallet.signAllTransactions( + solanaTransactions.map((txn) => { + return bufferToTransaction(Buffer.from(txn, 'base64')) + }), + ) + } + + if (solanaSignedTransactions) { + try { + // eslint-disable-next-line no-restricted-syntax + for (const txn of solanaSignedTransactions) { + // eslint-disable-next-line no-await-in-loop + await sendAndConfirmWithRetry( + anchorProvider.connection, + txn.serialize(), + { + skipPreflight: true, + }, + 'confirmed', + ) + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + wrapProgramError(e) + } + } + } + + const keyToAssetK = keyToAssetKey(DAO_KEY, tx.gateway.b58, 'b58')[0] + const keyToAsset = await hemProgram.account.keyToAssetV0.fetch(keyToAssetK) + const { asset } = keyToAsset + const collectable = await getHotspotWithRewards(asset, anchorProvider) + collectNav.navigate( + iotInfo ? 'HotspotDetailsScreen' : 'AssertLocationScreen', + { collectable }, + ) + }) + + return ( + + + {t('hotspotOnboarding.onboarding.subtitle')} + + {loading && } + {error && ( + + {error.message ? error.message.toString() : error.toString()} + + )} + + tabNav.push('Collectables')} + /> + + ) +} + +export default AddGatewayBle diff --git a/src/features/hotspot-onboarding/iot-ble/HotspotBLENav.tsx b/src/features/hotspot-onboarding/iot-ble/HotspotBLENav.tsx new file mode 100644 index 000000000..05f3d7e82 --- /dev/null +++ b/src/features/hotspot-onboarding/iot-ble/HotspotBLENav.tsx @@ -0,0 +1,51 @@ +import { HotspotBleProvider } from '@helium/react-native-sdk' +import { RouteProp, useRoute } from '@react-navigation/native' +import { + createNativeStackNavigator, + NativeStackNavigationOptions, +} from '@react-navigation/native-stack' +import * as React from 'react' +import { OnboardingtackParamList } from '../navTypes' +import AddGatewayBle from './AddGatewayBle' +import ScanHotspots from './ScanHotspots' +import WifiSettings from './WifiSettings' +import WifiSetup from './WifiSetup' +import { IotBleOptionsProvider } from './optionsContext' + +const Stack = createNativeStackNavigator() + +const screenOptions = { headerShown: false } as NativeStackNavigationOptions + +type Route = RouteProp +export default React.memo(function HotspotBLENav() { + const route = useRoute() + const iotParams = route.params + return ( + + + + + + + + + + + ) +}) diff --git a/src/features/hotspot-onboarding/iot-ble/ScanHotspots.tsx b/src/features/hotspot-onboarding/iot-ble/ScanHotspots.tsx new file mode 100644 index 000000000..88a17580b --- /dev/null +++ b/src/features/hotspot-onboarding/iot-ble/ScanHotspots.tsx @@ -0,0 +1,221 @@ +import BackScreen from '@components/BackScreen' +import ButtonPressable from '@components/ButtonPressable' +import CircleLoader from '@components/CircleLoader' +import FabButton from '@components/FabButton' +import Text from '@components/Text' +import TouchableOpacityBox from '@components/TouchableOpacityBox' +import { Device, useHotspotBle } from '@helium/react-native-sdk' +import { useNavigation } from '@react-navigation/native' +import React, { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { FlatList, Platform } from 'react-native' +import { + PERMISSIONS, + PermissionStatus, + RESULTS, + check, + request, +} from 'react-native-permissions' +import * as Logger from '../../../utils/logger' +import type { HotspotBleNavProp } from './navTypes' +import { useIotBleOptions } from './optionsContext' + +const ScanHotspots = () => { + const { startScan, stopScan, connect, scannedDevices } = useHotspotBle() + const [scanning, setScanning] = useState(false) + const { bleInstructions } = useIotBleOptions() + const [canScan, setCanScan] = useState(undefined) + const navigation = useNavigation() + const { t } = useTranslation() + const [error, setError] = useState(undefined) + + const showError = (e: any) => { + Logger.error(e) + setError(e.toString()) + } + + const updateCanScan = useCallback((result: PermissionStatus) => { + switch (result) { + case RESULTS.UNAVAILABLE: + case RESULTS.BLOCKED: + case RESULTS.DENIED: + case RESULTS.LIMITED: + setCanScan(false) + break + case RESULTS.GRANTED: + setCanScan(true) + break + } + }, []) + + useEffect(() => { + if (Platform.OS === 'ios') { + setCanScan(true) + return + } + + check(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION) + .then(updateCanScan) + .catch(showError) + }, [updateCanScan]) + + useEffect(() => { + if (canScan !== false) return + + request(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION) + .then(updateCanScan) + .catch(showError) + }, [canScan, updateCanScan]) + + const handleScanPress = useCallback(() => { + const shouldScan = !scanning + setScanning(shouldScan) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let timeout: any | undefined + if (shouldScan) { + setError(undefined) + timeout = setTimeout(() => { + stopScan() + setScanning(false) + if (scannedDevices.length === 0) { + setError( + 'No hotspots found. Please ensure bluetooth pairing is enabled', + ) + } + }, 30 * 1000) + } + + if (shouldScan) { + startScan((e) => { + if (e) { + showError(e) + } + }) + } else { + stopScan() + } + return () => { + if (timeout) { + clearTimeout(timeout) + } + } + }, [scannedDevices.length, scanning, startScan, stopScan]) + + const navNext = useCallback( + () => navigation.push('WifiSettings'), + [navigation], + ) + + const [connecting, setConnecting] = useState(false) + const connectDevice = useCallback( + (d: Device) => async () => { + try { + setConnecting(true) + await connect(d) + if (scanning) { + stopScan() + setScanning(false) + } + setConnecting(false) + navNext() + } catch (e) { + showError(e) + } finally { + setConnecting(false) + } + }, + [connect, navNext, scanning, stopScan], + ) + + const renderItem = React.useCallback( + // eslint-disable-next-line react/no-unused-prop-types + ({ item }: { item: Device }) => { + return ( + + + + {item.name} + + + ) + }, + [connectDevice, connecting], + ) + + const keyExtractor = React.useCallback(({ id }: Device) => id, []) + + return ( + + + {bleInstructions || t('hotspotOnboarding.scan.subtitle')} + + {scannedDevices.length === 0 && scanning && } + {scannedDevices.length === 0 && scanning && ( + + {t('hotspotOnboarding.scan.scanning')} + + )} + + + {error && ( + + {error} + + )} + + + ) +} + +export default ScanHotspots diff --git a/src/features/hotspot-onboarding/iot-ble/WifiSettings.tsx b/src/features/hotspot-onboarding/iot-ble/WifiSettings.tsx new file mode 100644 index 000000000..955718a6a --- /dev/null +++ b/src/features/hotspot-onboarding/iot-ble/WifiSettings.tsx @@ -0,0 +1,166 @@ +import BackScreen from '@components/BackScreen' +import ButtonPressable from '@components/ButtonPressable' +import FabButton from '@components/FabButton' +import Text from '@components/Text' +import TouchableOpacityBox from '@components/TouchableOpacityBox' +import { useHotspotBle } from '@helium/react-native-sdk' +import { useNavigation } from '@react-navigation/native' +import React, { useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { Alert, SectionList } from 'react-native' +import type { HotspotBleNavProp } from './navTypes' + +type Section = { + title: string + data: string[] + type: 'configured' | 'available' +} + +const WifiSettings = () => { + const navigation = useNavigation() + const { t } = useTranslation() + const navNext = useCallback( + () => navigation.push('AddGatewayBle'), + [navigation], + ) + const [networks, setNetworks] = useState() + const [configuredNetworks, setConfiguredNetworks] = useState() + const [connected, setConnected] = useState(false) + + const { isConnected, readWifiNetworks, removeConfiguredWifi } = + useHotspotBle() + + useEffect(() => { + isConnected().then(setConnected) + }, [isConnected]) + + useEffect(() => { + if (!connected) return + + readWifiNetworks(true).then(setConfiguredNetworks) + readWifiNetworks(false).then(setNetworks) + }, [connected, readWifiNetworks]) + + const handleNetworkSelected = useCallback( + ({ + network, + type, + }: { + network: string + type: 'configured' | 'available' + }) => + async () => { + if (type === 'available') { + navigation.push('WifiSetup', { network }) + } else { + Alert.alert( + t('hotspotOnboarding.wifiSettings.title'), + t('hotspotOnboarding.wifiSettings.remove', { network }), + [ + { + text: t('generic.cancel'), + style: 'default', + }, + { + text: t('generic.remove'), + style: 'destructive', + onPress: async () => { + setConfiguredNetworks( + configuredNetworks?.filter((n) => n !== network), + ) + await removeConfiguredWifi(network) + readWifiNetworks(true).then(setConfiguredNetworks) + readWifiNetworks(false).then(setNetworks) + }, + }, + ], + ) + } + }, + [configuredNetworks, navigation, readWifiNetworks, removeConfiguredWifi, t], + ) + + const renderItem = useCallback( + ({ + item: network, + section: { type }, + }: { + // eslint-disable-next-line react/no-unused-prop-types + item: string + // eslint-disable-next-line react/no-unused-prop-types + section: Section + }) => { + return ( + + + + {network} + + + ) + }, + [handleNetworkSelected], + ) + + const keyExtractor = useCallback((name: string) => name, []) + + const renderSectionHeader = ({ + section: { title }, + }: { + section: Section + }) => ( + + {title} + + ) + + const sections = useMemo( + (): Section[] => [ + { + data: configuredNetworks || [], + title: t('hotspotOnboarding.wifiSettings.configured'), + type: 'configured', + }, + { + data: networks || [], + title: t('hotspotOnboarding.wifiSettings.available'), + type: 'available', + }, + ], + [configuredNetworks, networks, t], + ) + + return ( + + + + + ) +} + +export default WifiSettings diff --git a/src/features/hotspot-onboarding/iot-ble/WifiSetup.tsx b/src/features/hotspot-onboarding/iot-ble/WifiSetup.tsx new file mode 100644 index 000000000..44ac434b5 --- /dev/null +++ b/src/features/hotspot-onboarding/iot-ble/WifiSetup.tsx @@ -0,0 +1,88 @@ +import BackScreen from '@components/BackScreen' +import Box from '@components/Box' +import ButtonPressable from '@components/ButtonPressable' +import Text from '@components/Text' +import TextInput from '@components/TextInput' +import { BleError, useHotspotBle } from '@helium/react-native-sdk' +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import type { HotspotBLEStackParamList, HotspotBleNavProp } from './navTypes' + +type Route = RouteProp +const WifiSetup = () => { + const { + params: { network }, + } = useRoute() + const [secureTextEntry, setSecureTextEntry] = useState(true) + const [loading, setLoading] = useState(false) + const [status, setStatus] = useState('') + const [password, setPassword] = useState('') + const { setWifi } = useHotspotBle() + const { t } = useTranslation() + const navigation = useNavigation() + const onBack = useCallback(() => { + if (navigation.canGoBack()) { + navigation.goBack() + } + }, [navigation]) + + const toggleSecureEntry = useCallback(() => { + setSecureTextEntry(!secureTextEntry) + }, [secureTextEntry]) + + const handleSetWifi = useCallback(async () => { + setLoading(true) + try { + const nextStatus = await setWifi(network, password) + setStatus(nextStatus) + onBack() + } catch (e) { + if (typeof e === 'string') { + setStatus(e) + } else { + setStatus((e as BleError).toString()) + } + } + setLoading(false) + }, [onBack, network, password, setWifi]) + + return ( + + + + + + + + {loading ? 'loading...' : status} + + ) +} + +export default WifiSetup diff --git a/src/features/hotspot-onboarding/iot-ble/navTypes.tsx b/src/features/hotspot-onboarding/iot-ble/navTypes.tsx new file mode 100644 index 000000000..c95457865 --- /dev/null +++ b/src/features/hotspot-onboarding/iot-ble/navTypes.tsx @@ -0,0 +1,13 @@ +import { NativeStackNavigationProp } from '@react-navigation/native-stack' +import { IotBleOptions } from '../navTypes' + +export type HotspotBLEStackParamList = { + ScanHotspots: IotBleOptions + WifiSettings: undefined + WifiSetup: { network: string } + AddGatewayBle: undefined + Diagnostics: undefined +} + +export type HotspotBleNavProp = + NativeStackNavigationProp diff --git a/src/features/hotspot-onboarding/iot-ble/optionsContext.tsx b/src/features/hotspot-onboarding/iot-ble/optionsContext.tsx new file mode 100644 index 000000000..8f56b8832 --- /dev/null +++ b/src/features/hotspot-onboarding/iot-ble/optionsContext.tsx @@ -0,0 +1,22 @@ +import React, { useContext } from 'react' +import { IotBleOptions } from '../navTypes' + +const IotBleOptionsContext = React.createContext({}) + +export const useIotBleOptions = () => { + return useContext(IotBleOptionsContext) +} + +export const IotBleOptionsProvider = ({ + value, + children, +}: { + value: IotBleOptions + children: React.ReactNode +}) => { + return ( + + {children} + + ) +} diff --git a/src/features/hotspot-onboarding/navTypes.tsx b/src/features/hotspot-onboarding/navTypes.tsx new file mode 100644 index 000000000..8c01db29a --- /dev/null +++ b/src/features/hotspot-onboarding/navTypes.tsx @@ -0,0 +1,21 @@ +import { NativeStackNavigationProp } from '@react-navigation/native-stack' + +export type IotBleOptions = { + bleInstructions?: string +} + +export type OnboardableDevice = { + name: string + type: 'IotBle' + image?: string + icon?: React.ReactElement + options: IotBleOptions +} + +export type OnboardingtackParamList = { + IotBle: IotBleOptions + SelectDevice: undefined +} + +export type OnboardingNavProp = + NativeStackNavigationProp diff --git a/src/features/payment/PaymentScreen.tsx b/src/features/payment/PaymentScreen.tsx index ec65b6e33..db89e0915 100644 --- a/src/features/payment/PaymentScreen.tsx +++ b/src/features/payment/PaymentScreen.tsx @@ -50,7 +50,6 @@ import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view import { useSafeAreaInsets } from 'react-native-safe-area-context' import Toast from 'react-native-simple-toast' import { useSelector } from 'react-redux' -import { useDebouncedCallback } from 'use-debounce' import useSubmitTxn from '../../hooks/useSubmitTxn' import { RootNavigationProp } from '../../navigation/rootTypes' import { useSolana } from '../../solana/SolanaProvider' diff --git a/src/hooks/useSubmitTxn.ts b/src/hooks/useSubmitTxn.ts index 92b9c1ef1..9f3a14c60 100644 --- a/src/hooks/useSubmitTxn.ts +++ b/src/hooks/useSubmitTxn.ts @@ -1,5 +1,6 @@ import { HotspotType } from '@helium/onboarding' -import { chunks } from '@helium/spl-utils' +import { useOnboarding } from '@helium/react-native-sdk' +import { chunks, sendAndConfirmWithRetry } from '@helium/spl-utils' import { PublicKey, Transaction } from '@solana/web3.js' import { useAccountStorage } from '@storage/AccountStorageProvider' import i18n from '@utils/i18n' @@ -18,8 +19,6 @@ import { sendDelegateDataCredits, sendMintDataCredits, sendTreasurySwap, - sendUpdateIotInfo, - sendUpdateMobileInfo, } from '../store/slices/solanaSlice' import { useAppDispatch } from '../store/store' import { @@ -33,6 +32,7 @@ export default () => { const { cluster, anchorProvider } = useSolana() const { t } = i18n const { walletSignBottomSheetRef } = useWalletSign() + const { getAssertData } = useOnboarding() const dispatch = useAppDispatch() @@ -461,58 +461,76 @@ export default () => { throw new Error(t('errors.account')) } - const updateInfoTxn = await solUtils.updateEntityInfoTxn({ - anchorProvider, - type, - entityKey, + const data = await getAssertData({ + decimalGain: decimalGain ? parseFloat(decimalGain) : undefined, + elevation: elevation ? parseFloat(elevation) : undefined, + gateway: entityKey, lat, lng, - elevation: elevation ? parseFloat(elevation) : undefined, - decimalGain: decimalGain ? parseFloat(decimalGain) : undefined, + owner: currentAccount.address, + hotspotTypes: [type], }) - const serializedTx = updateInfoTxn.serialize({ - requireAllSignatures: false, - }) + const serializedTxs = data.solanaTransactions?.map((txn) => + Buffer.from(txn, 'base64'), + ) const decision = await walletSignBottomSheetRef.show({ type: WalletStandardMessageTypes.signTransaction, url: '', additionalMessage: t('transactions.signAssertLocationTxn'), - serializedTxs: [Buffer.from(serializedTx)], + serializedTxs, }) if (!decision) { throw new Error('User rejected transaction') } - - if (type === 'iot') { - await dispatch( - sendUpdateIotInfo({ - account: currentAccount, - anchorProvider, - cluster, - updateTxn: updateInfoTxn, - }), - ) - } - - if (type === 'mobile') { - await dispatch( - sendUpdateMobileInfo({ - account: currentAccount, - anchorProvider, - cluster, - updateTxn: updateInfoTxn, - }), - ) + const signedTxns = + serializedTxs && + (await anchorProvider.wallet.signAllTransactions( + serializedTxs.map((ser) => Transaction.from(ser)), + )) + + try { + // eslint-disable-next-line no-restricted-syntax + for (const txn of signedTxns || []) { + // eslint-disable-next-line no-await-in-loop + await sendAndConfirmWithRetry( + anchorProvider.connection, + txn.serialize(), + { + skipPreflight: true, + }, + 'confirmed', + ) + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + if ( + e.toString().includes('Insufficient Balance') || + e.toString().includes('"Custom":1') || + e.InstructionError[1].Custom === 1 + ) { + if (data.isFree) { + throw new Error( + `Manufacturer ${data?.maker?.name} does not have enough SOL or Data Credits to assert location. Please contact the manufacturer of this hotspot to resolve this issue.`, + ) + } else { + throw new Error( + 'Insufficient balance of either HNT, Data Credits, or Sol to assert location', + ) + } + } + if (e.InstructionError) { + throw new Error(`Program Error: ${JSON.stringify(e)}`) + } + throw e } }, [ anchorProvider, - cluster, currentAccount, - dispatch, + getAssertData, t, walletSignBottomSheetRef, ], diff --git a/src/locales/en.ts b/src/locales/en.ts index db630f4d0..b132f2327 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -17,6 +17,39 @@ export default { errors: { accountNotSelected: 'There must be a wallet selected to submit a txn', }, + hotspotOnboarding: { + scan: { + title: 'Scan for Hotspots', + start: 'Start Scan', + stop: 'Stop Scan', + notEnabled: 'Bluetooth is not enabled', + scanning: 'Scanning for Hotspots', + }, + wifiSettings: { + title: 'Wifi Settings', + remove: 'Would you like to remove {{network}}?', + available: 'Available Networks', + configured: 'Configured Networks', + setup: 'Setup Wifi', + }, + onboarding: { + title: 'Onboarding', + subtitle: + 'Onboard your Hotspot to the IOT network. After onboarding this Hotspot, you will be able to set the location and antenna details.', + onboard: 'Onboard Hotspot', + hotspotNotFound: + 'This hotspot does not exist in the onboarding server. Contact your manufacturer to have them approve hotspot with id {{onboardAddress}}', + makerNotFound: 'Maker does not exist', + manufacturerMissingSol: + 'Manufacturer {{name}} does not have enough SOL to onboard this hotspot. Please contact the manufacturer to resolve this issue.', + manufacturerMissingDcOrSol: + 'Manufacturer {{name}} does not have enough Data Credits or SOL to onboard this hotspot. Please contact the manufacturer to resolve this issue.', + }, + selectOnboardingMethod: { + title: 'Select Onboarding Method', + subtitle: 'Select your onboarding method to continue.', + }, + }, accountImport: { accountLimit: 'You have reached the wallet limit.\nTo add another wallet, remove a wallet account and try again.', @@ -213,6 +246,7 @@ export default { hotspotCount_plural: '{{count}} Hotspots', chooseAmountOfHotspots: 'Choose amount of hotspots to show per page', filter: 'Filter', + new: 'Connect a Hotspot', currentDisplayedRewards: 'The rewards that are currently displayed as pending are only for the hotspots shown. Scroll to load more or click the filter to show more hotspots per page.', showAllHotspotsWarning: @@ -555,7 +589,9 @@ export default { insufficientBalance: 'Insufficient balance', ok: 'OK', period: '.', + password: 'Password', retry: 'Retry', + remove: 'Remove', share: 'Share', skip: 'Skip', success: 'Success', diff --git a/src/navigation/RootNavigator.tsx b/src/navigation/RootNavigator.tsx index 38eeb4c94..f124fc9bc 100644 --- a/src/navigation/RootNavigator.tsx +++ b/src/navigation/RootNavigator.tsx @@ -7,10 +7,10 @@ import { useColors } from '@theme/themeHooks' import React, { memo, useCallback, useEffect, useRef } from 'react' import changeNavigationBarColor from 'react-native-navigation-bar-color' import { useSelector } from 'react-redux' -import DappLoginScreen from '../features/dappLogin/DappLoginScreen' import ConnectedWallets, { ConnectedWalletsRef, } from '../features/account/ConnectedWallets' +import DappLoginScreen from '../features/dappLogin/DappLoginScreen' import { HomeNavigationProp } from '../features/home/homeTypes' import OnboardingNavigator from '../features/onboarding/OnboardingNavigator' import ImportPrivateKey from '../features/onboarding/import/ImportPrivateKey' diff --git a/src/navigation/TabBarNavigator.tsx b/src/navigation/TabBarNavigator.tsx index 2134934bc..af6df1520 100644 --- a/src/navigation/TabBarNavigator.tsx +++ b/src/navigation/TabBarNavigator.tsx @@ -6,7 +6,7 @@ import { } from '@react-navigation/bottom-tabs' import { Edge, useSafeAreaInsets } from 'react-native-safe-area-context' import Dollar from '@assets/images/dollar.svg' -import Gem from '@assets/images/gem.svg' +import Gem from '@assets/images/collectableIcon.svg' import Transactions from '@assets/images/transactions.svg' import Notifications from '@assets/images/notifications.svg' import { Portal } from '@gorhom/portal' diff --git a/src/solana/SolanaProvider.tsx b/src/solana/SolanaProvider.tsx index bf3259f25..329a8c1c5 100644 --- a/src/solana/SolanaProvider.tsx +++ b/src/solana/SolanaProvider.tsx @@ -5,6 +5,7 @@ import { init as initHem } from '@helium/helium-entity-manager-sdk' import { init as initHsd } from '@helium/helium-sub-daos-sdk' import { init as initLazy } from '@helium/lazy-distributor-sdk' import { DC_MINT, HNT_MINT } from '@helium/spl-utils' +import { SolanaProvider as SolanaProviderRnHelium } from '@helium/react-native-sdk' import { AccountInfo, Cluster, @@ -190,7 +191,19 @@ const SolanaContext = const { Provider } = SolanaContext const SolanaProvider = ({ children }: { children: ReactNode }) => { - return {children} + const value = useSolanaHook() + return ( + + {value.connection && ( + + {children} + + )} + + ) } export const useSolana = (): SolanaManager => useContext(SolanaContext) diff --git a/src/store/slices/solanaSlice.ts b/src/store/slices/solanaSlice.ts index ea372df67..0a2a41c93 100644 --- a/src/store/slices/solanaSlice.ts +++ b/src/store/slices/solanaSlice.ts @@ -123,20 +123,6 @@ type DelegateDataCreditsInput = { delegateDCTxn: Transaction } -type UpdateIotInfoInput = { - account: CSAccount - anchorProvider: AnchorProvider - cluster: Cluster - updateTxn: Transaction -} - -type UpdateMobileInfoInput = { - account: CSAccount - anchorProvider: AnchorProvider - cluster: Cluster - updateTxn: Transaction -} - export const makePayment = createAsyncThunk( 'solana/makePayment', async ({ account, cluster, anchorProvider, paymentTxns }: PaymentInput) => { @@ -520,38 +506,6 @@ export const getTxns = createAsyncThunk( }, ) -export const sendUpdateIotInfo = createAsyncThunk( - 'solana/sendUpdateIotInfo', - async ({ cluster, anchorProvider, updateTxn }: UpdateIotInfoInput) => { - try { - const signed = await anchorProvider.wallet.signTransaction(updateTxn) - const sig = await anchorProvider.sendAndConfirm(signed) - - postPayment({ signatures: [sig], cluster }) - } catch (error) { - Logger.error(error) - throw error - } - return true - }, -) - -export const sendUpdateMobileInfo = createAsyncThunk( - 'solana/sendUpdateMobileInfo', - async ({ cluster, anchorProvider, updateTxn }: UpdateMobileInfoInput) => { - try { - const signed = await anchorProvider.wallet.signTransaction(updateTxn) - const sig = await anchorProvider.sendAndConfirm(signed) - - postPayment({ signatures: [sig], cluster }) - } catch (error) { - Logger.error(error) - throw error - } - return true - }, -) - const solanaSlice = createSlice({ name: 'solana', initialState, @@ -842,32 +796,6 @@ const solanaSlice = createSlice({ state.activity.error = error } }) - builder.addCase(sendUpdateIotInfo.rejected, (state, action) => { - state.payment = { success: false, loading: false, error: action.error } - }) - builder.addCase(sendUpdateIotInfo.pending, (state, _action) => { - state.payment = { success: false, loading: true, error: undefined } - }) - builder.addCase(sendUpdateIotInfo.fulfilled, (state, _action) => { - state.payment = { - success: true, - loading: false, - error: undefined, - } - }) - builder.addCase(sendUpdateMobileInfo.rejected, (state, action) => { - state.payment = { success: false, loading: false, error: action.error } - }) - builder.addCase(sendUpdateMobileInfo.pending, (state, _action) => { - state.payment = { success: false, loading: true, error: undefined } - }) - builder.addCase(sendUpdateMobileInfo.fulfilled, (state, _action) => { - state.payment = { - success: true, - loading: false, - error: undefined, - } - }) }, }) diff --git a/src/utils/solanaUtils.ts b/src/utils/solanaUtils.ts index fa4c6801a..8981477ae 100644 --- a/src/utils/solanaUtils.ts +++ b/src/utils/solanaUtils.ts @@ -983,6 +983,18 @@ export const getCompressedCollectables = async ( return items as CompressedNFT[] } +export const getHotspotWithRewards = async ( + assetId: PublicKey, + anchorProvider: AnchorProvider, +): Promise => { + const conn = anchorProvider.connection as WrappedConnection + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const asset = ((await conn.getAsset(assetId.toBase58())) as any).result + + const withMetadata = await getCompressedNFTMetadata([asset as CompressedNFT]) + return (await annotateWithPendingRewards(anchorProvider, withMetadata))[0] +} + export const getCompressedCollectablesByCreator = async ( pubKey: PublicKey, anchorProvider: AnchorProvider, diff --git a/tsconfig.json b/tsconfig.json index 38c51bba8..7e9ef5dca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,7 @@ "@assets": ["./src/assets"], "@components/*": ["./src/components/*"], "@constants/*": ["./src/constants/*"], + "@components": ["src/components/index"], "@hooks/*": ["./src/hooks/*"], "@theme/*": ["./src/theme/*"], "@utils/*": ["./src/utils/*"], diff --git a/yarn.lock b/yarn.lock index 103179c79..50196a752 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,6 +18,11 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@azure/core-asynciterator-polyfill@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" + integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== + "@babel/code-frame@7.10.4", "@babel/code-frame@~7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" @@ -2366,6 +2371,18 @@ bn.js "^5.2.0" bs58 "^4.0.1" +"@helium/circuit-breaker-sdk@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@helium/circuit-breaker-sdk/-/circuit-breaker-sdk-0.1.4.tgz#53a49a70d533540e4118c19f2d04cd63c556b390" + integrity sha512-9Fa1zxhYO9Nh+iZuPgA4dpyAp9Le2TRoVRu/caWWDC8DNC9Ba2Hd/xeWbRDExymryVZqq741U57OiAi3cPXwbQ== + dependencies: + "@coral-xyz/anchor" "^0.26.0" + "@helium/idls" "^0.1.1" + "@helium/spl-utils" "^0.1.4" + "@solana/spl-token" "^0.3.6" + bn.js "^5.2.0" + bs58 "^4.0.1" + "@helium/circuit-breaker-sdk@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@helium/circuit-breaker-sdk/-/circuit-breaker-sdk-0.3.2.tgz#e2819e1aceeb7d934617085995911e3e97ad5d50" @@ -2387,7 +2404,7 @@ react-native-sodium "^0.4.0" safe-buffer "^5.2.1" -"@helium/currency-utils@0.1.1": +"@helium/currency-utils@0.1.1", "@helium/currency-utils@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@helium/currency-utils/-/currency-utils-0.1.1.tgz#20398359f5b44c805a35eae1a4655ecbabc7b35e" integrity sha512-3HyB4qu6HQ1UCQepF02n7fmw2YVUE+o6k//1Dy/ZQFqV9xlk/CaxguZ72MJC9nZaUD+im0pT2ZQjFoViGNRyrw== @@ -2403,6 +2420,21 @@ dependencies: bignumber.js "^9.0.0" +"@helium/data-credits-sdk@^0.1.3": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@helium/data-credits-sdk/-/data-credits-sdk-0.1.4.tgz#8ba9b9bde64ed65911d03bf6c77392c2c040c3a1" + integrity sha512-CgubrCg/iHWNPRW4G9XhHLqIppnrhddwSbcaeQlPT1B+R+CW7Zeft9bFqcamCge1l2/3vxzHlrKz5kR5tLRIVw== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@helium/circuit-breaker-sdk" "^0.1.4" + "@helium/helium-sub-daos-sdk" "^0.1.4" + "@helium/idls" "^0.1.1" + "@helium/spl-utils" "^0.1.4" + "@solana/spl-token" "^0.3.6" + bn.js "^5.2.0" + bs58 "^4.0.1" + crypto-js "^4.1.1" + "@helium/data-credits-sdk@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@helium/data-credits-sdk/-/data-credits-sdk-0.3.2.tgz#060a28a4bf46c19f10417aa490b7ba45f7e44059" @@ -2458,6 +2490,25 @@ bn.js "^5.2.0" bs58 "^4.0.1" +"@helium/helium-entity-manager-sdk@^0.1.3", "@helium/helium-entity-manager-sdk@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@helium/helium-entity-manager-sdk/-/helium-entity-manager-sdk-0.1.5.tgz#96457885d125c781831a1b595b3cb5e3d6a91f1d" + integrity sha512-1zvavQVXDXmH5IFKYJJwGPxV4iMPQYiGJKk6do1jGFFizfGelDD7raUq+csnMiNExYQeOT8vQSyjNpQuYKjU2g== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@helium/address" "^4.6.2" + "@helium/helium-sub-daos-sdk" "^0.1.4" + "@helium/idls" "^0.1.1" + "@helium/spl-utils" "^0.1.4" + "@metaplex-foundation/mpl-bubblegum" "^0.6.2" + "@metaplex-foundation/mpl-token-metadata" "^2.2.3" + "@solana/spl-account-compression" "^0.1.5" + "@solana/spl-token" "^0.3.6" + bn.js "^5.2.0" + bs58 "^4.0.1" + crypto-js "^4.1.1" + js-sha256 "^0.9.0" + "@helium/helium-entity-manager-sdk@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@helium/helium-entity-manager-sdk/-/helium-entity-manager-sdk-0.3.2.tgz#12ba42ac1682a6e2c2acd1e5a029c9ed4e13a48d" @@ -2488,6 +2539,19 @@ pako "^2.0.3" react-async-hook "^4.0.0" +"@helium/helium-sub-daos-sdk@^0.1.3", "@helium/helium-sub-daos-sdk@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@helium/helium-sub-daos-sdk/-/helium-sub-daos-sdk-0.1.4.tgz#99852310f0f8fa4e7afa2d128f3d2eff884b65d4" + integrity sha512-O7OiEYrZeLBHJJAdzPuG3JygrZ4i+cb3l5QnyQ+pIVpunuOfsA+fNpzgzDH2MBE9MDUkOr3kR3uSF3Jy3DA9ww== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@helium/circuit-breaker-sdk" "^0.1.4" + "@helium/spl-utils" "^0.1.4" + "@helium/treasury-management-sdk" "^0.1.4" + "@helium/voter-stake-registry-sdk" "^0.1.4" + bn.js "^5.2.0" + bs58 "^4.0.1" + "@helium/helium-sub-daos-sdk@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@helium/helium-sub-daos-sdk/-/helium-sub-daos-sdk-0.3.2.tgz#9e7550e8895fbb56fb7a807e0f476f67d53e248b" @@ -2501,6 +2565,19 @@ bn.js "^5.2.0" bs58 "^4.0.1" +"@helium/hotspot-utils@^0.1.3": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@helium/hotspot-utils/-/hotspot-utils-0.1.5.tgz#e93ff9a15d7678c28a696ef77d617478eb83cfd7" + integrity sha512-bF0gMlkZk7DYJ7ugxvhNW/AKWlDM+hrxh+sm6MN67kNiNlGsqdAU+LwgoZ4Z5/C2FWNMgNqUMTE4CQIJRshubg== + dependencies: + "@coral-xyz/anchor" "^0.26.0" + "@helium/helium-entity-manager-sdk" "^0.1.5" + "@helium/helium-sub-daos-sdk" "^0.1.4" + "@helium/idls" "^0.1.1" + "@helium/spl-utils" "^0.1.4" + "@solana/web3.js" "^1.73.0" + bs58 "^5.0.0" + "@helium/http@4.7.5": version "4.7.5" resolved "https://registry.yarnpkg.com/@helium/http/-/http-4.7.5.tgz#c78ffcba77d29b9ef5514adfd41c08412ee8a8d3" @@ -2514,6 +2591,17 @@ retry-axios "^2.1.2" snakecase-keys "^5.1.0" +"@helium/idls@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@helium/idls/-/idls-0.0.26.tgz#379890824915edc3bdfb69bdf1db3936e17cb500" + integrity sha512-SpSmogohcQ0ZWcGmvx43RpGd3optlXmZmkXnZ5sVzbjq3bYwh2usVRMeVmzrrXsuT3uZaftofUXGpJi9qY4RtQ== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@solana/web3.js" "^1.43.4" + bn.js "^5.2.0" + borsh "^0.7.0" + bs58 "^4.0.1" + "@helium/idls@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@helium/idls/-/idls-0.1.1.tgz#dd4ca538a25dd04c828e4696b784a971702a2dc9" @@ -2556,6 +2644,15 @@ axios-retry "^3.4.0" qs "^6.10.3" +"@helium/onboarding@^4.10.0": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@helium/onboarding/-/onboarding-4.10.2.tgz#5991f0c0c6d15ebbddeaf92ddf5be7625da3bf73" + integrity sha512-Kbh7WD7Fu/AEqHvn0GVhhusN0Zpp+h7ENFjq5+NKEOOyQj7k3m5skSwl1RmwPqpO58QdBM4DKNvBASX7XNuXjA== + dependencies: + axios "^0.24.0" + axios-retry "^3.4.0" + qs "^6.10.3" + "@helium/proto-ble@4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@helium/proto-ble/-/proto-ble-4.0.0.tgz#dd2800d0dbbfc793ea5cb6b45e7613f490f8a4d8" @@ -2563,6 +2660,13 @@ dependencies: protobufjs "^6.8.9" +"@helium/proto-ble@^4.8.1": + version "4.11.1" + resolved "https://registry.yarnpkg.com/@helium/proto-ble/-/proto-ble-4.11.1.tgz#23de2c6d112b77001d3c09808c5e4d6a6847f323" + integrity sha512-k3tvz+YmcXlGMcCLsjEagEyuQQvRD9Bfs8tzO5apgPI/ilbJQrjj5Oc6iv95TYj1RTJt1pdlDcc+qVI6y5m1uw== + dependencies: + protobufjs "^6.8.9" + "@helium/proto@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@helium/proto/-/proto-1.6.0.tgz#b97003bbc511afec1abac7cbffc5c088e348022d" @@ -2570,10 +2674,38 @@ dependencies: protobufjs "^6.11.3" -"@helium/react-native-sdk@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@helium/react-native-sdk/-/react-native-sdk-1.0.0.tgz#41024fa99859490bd8a0b717f52acc11ae72f114" - integrity sha512-Qi1Nnp/q2hsz2D7aeuM6LxXhNX8NrHz1U+PoQslwK2XfqPFZEYb4uAzjXDKlc+JBWPiF96GMJywv/ofxlZ9XLg== +"@helium/react-native-sdk@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@helium/react-native-sdk/-/react-native-sdk-2.1.4.tgz#fc32db2430d058bb1aa8b8dff2c9f3ee7599e632" + integrity sha512-a4bojy41vrFeHfvFcxtxt+Nsy/llS7k5GSOm/cmwIpy2r4SU3ScuNjgAaCsClmZ+A/OzTrjJMCbbKQdCq9cP4g== + dependencies: + "@azure/core-asynciterator-polyfill" "^1.0.2" + "@coral-xyz/anchor" "^0.26.0" + "@helium/currency-utils" "^0.1.1" + "@helium/data-credits-sdk" "^0.1.3" + "@helium/helium-entity-manager-sdk" "^0.1.3" + "@helium/helium-sub-daos-sdk" "^0.1.3" + "@helium/hotspot-utils" "^0.1.3" + "@helium/onboarding" "^4.10.0" + "@helium/proto-ble" "^4.8.1" + "@helium/spl-utils" "^0.1.3" + "@helium/voter-stake-registry-sdk" "^0.0.26" + "@metaplex-foundation/beet" "^0.7.1" + "@metaplex-foundation/beet-solana" "^0.4.0" + "@metaplex-foundation/mpl-bubblegum" "^0.6.2" + "@metaplex-foundation/mpl-token-metadata" "^2.5.2" + "@pythnetwork/client" "^2.11.0" + "@solana/spl-token" "^0.3.6" + "@solana/web3.js" "^1.69.0" + assert "^2.0.0" + axios "^1.3.0" + axios-retry "^3.3.1" + bignumber.js "^9.1.1" + bn.js "^5.2.1" + bs58 "^5.0.0" + react-native-crypto "^2.2.0" + text-encoding-polyfill "^0.6.7" + typescript-collections "^1.3.3" "@helium/rewards-oracle-sdk@^0.3.2": version "0.3.2" @@ -2586,6 +2718,19 @@ bn.js "^5.2.0" bs58 "^4.0.1" +"@helium/spl-utils@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@helium/spl-utils/-/spl-utils-0.0.26.tgz#539adfa29b7c043296ea01af02896a7a37219ee9" + integrity sha512-P++DOlzoqNkgGgMIo0yH2NqLAxjVAjy9IoP1V7jV+w5VCZEzyqRn+VTmSOwaH2WMK6qSosk6ex1xOQa/yalmEA== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@helium/address" "^4.8.1" + "@solana/spl-token" "^0.3.6" + "@solana/web3.js" "^1.43.4" + bn.js "^5.2.0" + borsh "^0.7.0" + bs58 "^4.0.1" + "@helium/spl-utils@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@helium/spl-utils/-/spl-utils-0.1.2.tgz#e12b924bf4bd3217f265250a2720cb7ed2316d1d" @@ -2600,6 +2745,20 @@ borsh "^0.7.0" bs58 "^5.0.0" +"@helium/spl-utils@^0.1.3", "@helium/spl-utils@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@helium/spl-utils/-/spl-utils-0.1.4.tgz#214b6076e76d2cd0095758ed3db33f0824048df3" + integrity sha512-QhEhJuOd9P8GbUKx5f9zI1m2zjN9si/IrAlDQk4gkFBDFsi4szzY03rj4CwyhmwIYJk/qi1b4JiMoRIinFutJg== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@helium/address" "^4.8.1" + "@solana/spl-account-compression" "^0.1.7" + "@solana/spl-token" "^0.3.6" + "@solana/web3.js" "^1.43.4" + bn.js "^5.2.0" + borsh "^0.7.0" + bs58 "^5.0.0" + "@helium/spl-utils@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@helium/spl-utils/-/spl-utils-0.3.2.tgz#bfafe11ce878c3d79694db523148c8738cc2ae41" @@ -2641,6 +2800,19 @@ bn.js "^5.2.0" bs58 "^4.0.1" +"@helium/treasury-management-sdk@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@helium/treasury-management-sdk/-/treasury-management-sdk-0.1.4.tgz#6d3ad274c3d3a7209ebeca6f901f9356e62c973a" + integrity sha512-w7hUTsP+kMMH5f0M/0VqOQ2KzdRACuY5qDHPt4X7VvjgjWFnps/mIHBXV1P2hG2YZDN9CiCSMwwjT9MFHISUiA== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@helium/circuit-breaker-sdk" "^0.1.4" + "@helium/idls" "^0.1.1" + "@helium/spl-utils" "^0.1.4" + "@solana/spl-token" "^0.3.6" + bn.js "^5.2.0" + bs58 "^4.0.1" + "@helium/treasury-management-sdk@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@helium/treasury-management-sdk/-/treasury-management-sdk-0.3.2.tgz#a6ec736ec2e7d282164084ab2dfbd2cff39b4078" @@ -2666,6 +2838,32 @@ bn.js "^5.2.0" bs58 "^4.0.1" +"@helium/voter-stake-registry-sdk@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@helium/voter-stake-registry-sdk/-/voter-stake-registry-sdk-0.0.26.tgz#5b54ef6344e9b272850bdd29001c38902a43c7ed" + integrity sha512-pPB/YX6LIMuiZNBiQBTavlgspFHm35SNo2nbtk9CmGpgowLX0tVcGMcCV67Qa5c/s82vf9GCqsmdghVBh+GE/Q== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@helium/idls" "^0.0.26" + "@helium/spl-utils" "^0.0.26" + "@metaplex-foundation/mpl-token-metadata" "^2.2.3" + "@solana/spl-token" "^0.3.6" + bn.js "^5.2.0" + bs58 "^4.0.1" + +"@helium/voter-stake-registry-sdk@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@helium/voter-stake-registry-sdk/-/voter-stake-registry-sdk-0.1.4.tgz#41d46d1b0364c710aff51df756ed5a2521bf96e7" + integrity sha512-8f+dWaS1IbSuybrvyvchuOd/NP9fCx8jCVyl02pKkURFZC0WdPckiaw+5kh2/y29nwwZJlVqdu7I7C2TR/6uyQ== + dependencies: + "@coral-xyz/anchor" "0.26.0" + "@helium/idls" "^0.1.1" + "@helium/spl-utils" "^0.1.4" + "@metaplex-foundation/mpl-token-metadata" "^2.2.3" + "@solana/spl-token" "^0.3.6" + bn.js "^5.2.0" + bs58 "^4.0.1" + "@helium/voter-stake-registry-sdk@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@helium/voter-stake-registry-sdk/-/voter-stake-registry-sdk-0.3.2.tgz#3d9f36cfd52ff597be56a6f10fb565790d6f8811" @@ -3235,6 +3433,21 @@ bn.js "^5.2.0" js-sha3 "^0.8.0" +"@metaplex-foundation/mpl-bubblegum@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-bubblegum/-/mpl-bubblegum-0.6.2.tgz#e1b098ccef10899b0d759a03e3d4b1ae7bdc9f0c" + integrity sha512-4tF7/FFSNtpozuIGD7gMKcqK2D49eVXZ144xiowC5H1iBeu009/oj2m8Tj6n4DpYFKWJ2JQhhhk0a2q7x0Begw== + dependencies: + "@metaplex-foundation/beet" "0.7.1" + "@metaplex-foundation/beet-solana" "0.4.0" + "@metaplex-foundation/cusper" "^0.0.2" + "@metaplex-foundation/mpl-token-metadata" "^2.5.2" + "@solana/spl-account-compression" "^0.1.4" + "@solana/spl-token" "^0.1.8" + "@solana/web3.js" "^1.50.1" + bn.js "^5.2.0" + js-sha3 "^0.8.0" + "@metaplex-foundation/mpl-bubblegum@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-bubblegum/-/mpl-bubblegum-0.7.0.tgz#b34067ad4fe846ceb60e47e49f221ecf4730add7" @@ -3441,6 +3654,15 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@pythnetwork/client@^2.11.0", "@pythnetwork/client@^2.19.0": + version "2.19.0" + resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.19.0.tgz#4b3b4fccb402d93ff00c01beec633e6f2bac94fe" + integrity sha512-0VSQ0NqBOa5EtloXbOVYZ6Wpu8CLP3oaOKVTaUMSX/HXbB00S6G+xdwF7stxo6emgrAMopotx3icEVug5Lpomg== + dependencies: + "@coral-xyz/anchor" "^0.28.1-beta.1" + "@coral-xyz/borsh" "^0.28.0" + buffer "^6.0.1" + "@pythnetwork/client@^2.12.0", "@pythnetwork/client@^2.17.0": version "2.17.0" resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.17.0.tgz#b155af06958f4b729bfee1c07130c556598cf168" @@ -3450,15 +3672,6 @@ "@coral-xyz/borsh" "^0.26.0" buffer "^6.0.1" -"@pythnetwork/client@^2.19.0": - version "2.19.0" - resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.19.0.tgz#4b3b4fccb402d93ff00c01beec633e6f2bac94fe" - integrity sha512-0VSQ0NqBOa5EtloXbOVYZ6Wpu8CLP3oaOKVTaUMSX/HXbB00S6G+xdwF7stxo6emgrAMopotx3icEVug5Lpomg== - dependencies: - "@coral-xyz/anchor" "^0.28.1-beta.1" - "@coral-xyz/borsh" "^0.28.0" - buffer "^6.0.1" - "@randlabs/communication-bridge@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@randlabs/communication-bridge/-/communication-bridge-1.0.1.tgz#d1ecfc29157afcbb0ca2d73122d67905eecb5bf3" @@ -3890,6 +4103,18 @@ js-sha3 "^0.8.0" typescript-collections "^1.3.3" +"@solana/spl-account-compression@^0.1.5": + version "0.1.10" + resolved "https://registry.yarnpkg.com/@solana/spl-account-compression/-/spl-account-compression-0.1.10.tgz#b3135ce89349d6090832b3b1d89095badd57e969" + integrity sha512-IQAOJrVOUo6LCgeWW9lHuXo6JDbi4g3/RkQtvY0SyalvSWk9BIkHHe4IkAzaQw8q/BxEVBIjz8e9bNYWIAESNw== + dependencies: + "@metaplex-foundation/beet" "^0.7.1" + "@metaplex-foundation/beet-solana" "^0.4.0" + bn.js "^5.2.1" + borsh "^0.7.0" + js-sha3 "^0.8.0" + typescript-collections "^1.3.3" + "@solana/spl-memo@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@solana/spl-memo/-/spl-memo-0.2.3.tgz#594a28c37b40c0e22143f38f71b4f56d1f5b24fd" @@ -4059,45 +4284,45 @@ rpc-websockets "^7.5.1" superstruct "^0.14.2" -"@solana/web3.js@^1.73.0": - version "1.76.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.76.0.tgz#0f888e25d727d0dadf3dd8a01967347555200b2b" - integrity sha512-aJtF/nTs+9St+KtTK/wgVJ+SinfjYzn+3w1ygYIPw8ST6LH+qHBn8XkodgDTwlv/xzNkaVz1kkUDOZ8BPXyZWA== +"@solana/web3.js@^1.69.0", "@solana/web3.js@^1.78.4": + version "1.78.5" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.78.5.tgz#591cd47423cdb0b5e5cb7e8dc4dc70b2abe02f80" + integrity sha512-2ZHsDNqkKdglJQrIvJ3p2DmgS3cGnary3VJyqt9C1SPrpAtLYzcElr3xyXJOznyQTU/8AMw+GoF11lFoKbicKg== dependencies: - "@babel/runtime" "^7.12.5" + "@babel/runtime" "^7.22.6" "@noble/curves" "^1.0.0" - "@noble/hashes" "^1.3.0" + "@noble/hashes" "^1.3.1" "@solana/buffer-layout" "^4.0.0" - agentkeepalive "^4.2.1" + agentkeepalive "^4.3.0" bigint-buffer "^1.1.5" - bn.js "^5.0.0" + bn.js "^5.2.1" borsh "^0.7.0" bs58 "^4.0.1" buffer "6.0.3" fast-stable-stringify "^1.0.0" - jayson "^3.4.4" - node-fetch "^2.6.7" + jayson "^4.1.0" + node-fetch "^2.6.12" rpc-websockets "^7.5.1" superstruct "^0.14.2" -"@solana/web3.js@^1.78.4": - version "1.78.5" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.78.5.tgz#591cd47423cdb0b5e5cb7e8dc4dc70b2abe02f80" - integrity sha512-2ZHsDNqkKdglJQrIvJ3p2DmgS3cGnary3VJyqt9C1SPrpAtLYzcElr3xyXJOznyQTU/8AMw+GoF11lFoKbicKg== +"@solana/web3.js@^1.73.0": + version "1.76.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.76.0.tgz#0f888e25d727d0dadf3dd8a01967347555200b2b" + integrity sha512-aJtF/nTs+9St+KtTK/wgVJ+SinfjYzn+3w1ygYIPw8ST6LH+qHBn8XkodgDTwlv/xzNkaVz1kkUDOZ8BPXyZWA== dependencies: - "@babel/runtime" "^7.22.6" + "@babel/runtime" "^7.12.5" "@noble/curves" "^1.0.0" - "@noble/hashes" "^1.3.1" + "@noble/hashes" "^1.3.0" "@solana/buffer-layout" "^4.0.0" - agentkeepalive "^4.3.0" + agentkeepalive "^4.2.1" bigint-buffer "^1.1.5" - bn.js "^5.2.1" + bn.js "^5.0.0" borsh "^0.7.0" bs58 "^4.0.1" buffer "6.0.3" fast-stable-stringify "^1.0.0" - jayson "^4.1.0" - node-fetch "^2.6.12" + jayson "^3.4.4" + node-fetch "^2.6.7" rpc-websockets "^7.5.1" superstruct "^0.14.2" @@ -5674,6 +5899,17 @@ assert@1.5.0: object-assign "^4.1.1" util "0.10.3" +assert@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== + dependencies: + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" + assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" @@ -5782,6 +6018,14 @@ axe-core@^4.4.3: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== +axios-retry@^3.3.1: + version "3.8.0" + resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.8.0.tgz#a174af633ef143a9f5642b9e4afe65c2017936b5" + integrity sha512-CfIsQyWNc5/AE7x/UEReRUadiBmQeoBpSEC+4QyGLJMswTsP1tz0GW2YYPnE7w9+ESMef5zOgLDFpHynNyEZ1w== + dependencies: + "@babel/runtime" "^7.15.4" + is-retry-allowed "^2.2.0" + axios-retry@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.4.0.tgz#f464dbe9408e5aa78fa319afd38bb69b533d8854" @@ -5820,7 +6064,7 @@ axios@^0.25.0: dependencies: follow-redirects "^1.14.7" -axios@^1.3.6: +axios@^1.3.0, axios@^1.3.6: version "1.5.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.0.tgz#f02e4af823e2e46a9768cfc74691fdd0517ea267" integrity sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ== @@ -6116,6 +6360,11 @@ bignumber.js@9.1.1, bignumber.js@^9.0.0, bignumber.js@^9.0.1, bignumber.js@^9.0. resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== +bignumber.js@^9.1.1: + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -9922,6 +10171,14 @@ is-invalid-path@^0.1.0: dependencies: is-glob "^2.0.0" +is-nan@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -12322,7 +12579,7 @@ object-inspect@^1.12.3, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== -object-is@^1.0.1: +object-is@^1.0.1, object-is@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== @@ -13429,7 +13686,7 @@ react-native-config@1.4.6: resolved "https://registry.yarnpkg.com/react-native-config/-/react-native-config-1.4.6.tgz#2aefebf4d9cf02831e64bbc1307596bd212f6d42" integrity sha512-cSLdOfva2IPCxh6HjHN1IDVW9ratAvNnnAUx6ar2Byvr8KQU7++ysdFYPaoNVuJURuYoAKgvjab8ZcnwGZIO6Q== -react-native-crypto@2.2.0: +react-native-crypto@2.2.0, react-native-crypto@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/react-native-crypto/-/react-native-crypto-2.2.0.tgz#c999ed7c96064f830e1f958687f53d0c44025770" integrity sha512-eZu9Y8pa8BN9FU2pIex7MLRAi+Cd1Y6bsxfiufKh7sfraAACJvjQTeW7/zcQAT93WMfM+D0OVk+bubvkrbrUkw== @@ -13567,6 +13824,11 @@ react-native-pager-view@6.1.2: resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.1.2.tgz#3522079b9a9d6634ca5e8d153bc0b4d660254552" integrity sha512-qs2KSFc+7N7B+UZ6SG2sTvCkppagm5fVyRclv1KFKc7lDtrhXLzN59tXJw575LDP/dRJoXsNwqUAhZJdws6ABQ== +react-native-permissions@^3.9.0: + version "3.9.2" + resolved "https://registry.yarnpkg.com/react-native-permissions/-/react-native-permissions-3.9.2.tgz#729ee7fe9ca437ee8e87f1ecec59acfe89bbb840" + integrity sha512-mBGoRDKi4twYH+UsPomPdi0dLpV6dGLmKzLpIgB5cLK/dzfM8Sr02Kch1xghVwT13ZkyCyd8DpcL4HgBJ4GQQg== + react-native-qrcode-svg@6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/react-native-qrcode-svg/-/react-native-qrcode-svg-6.1.2.tgz#a7cb6c10199ab01418a7f7700ce17a6a014f544e" @@ -15282,7 +15544,7 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-encoding-polyfill@0.6.7: +text-encoding-polyfill@0.6.7, text-encoding-polyfill@^0.6.7: version "0.6.7" resolved "https://registry.yarnpkg.com/text-encoding-polyfill/-/text-encoding-polyfill-0.6.7.tgz#4d27de0153e4c86eb2631ffd74c2f3f57969a9ec" integrity sha512-/DZ1XJqhbqRkCop6s9ZFu8JrFRwmVuHg4quIRm+ziFkR3N3ec6ck6yBvJ1GYeEQZhLVwRW0rZE+C3SSJpy0RTg== @@ -15843,7 +16105,7 @@ util@^0.10.3: dependencies: inherits "2.0.3" -util@^0.12.4: +util@^0.12.4, util@^0.12.5: version "0.12.5" resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==