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==