diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 4a38b3db..ea3cca2a 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -2046,9 +2046,9 @@ SPEC CHECKSUMS:
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
VisionCamera: cb84d0d8485b3e67c91b62931d3aa88f49747c92
- Yoga: 950bbfd7e6f04790fdb51149ed51df41f329fcc8
+ Yoga: 2246eea72aaf1b816a68a35e6e4b74563653ae09
ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5
PODFILE CHECKSUM: af8e0f0904c0f9cc43a95eef4e1feb504870ee6e
-COCOAPODS: 1.16.1
+COCOAPODS: 1.15.2
diff --git a/src/app/services/HotspotService/pages/ClaimTokensPage.tsx b/src/app/services/HotspotService/pages/ClaimTokensPage.tsx
index d8d34640..8d497536 100644
--- a/src/app/services/HotspotService/pages/ClaimTokensPage.tsx
+++ b/src/app/services/HotspotService/pages/ClaimTokensPage.tsx
@@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next'
import { Image, RefreshControl } from 'react-native'
import MobileIcon from '@assets/svgs/mobileIconNew.svg'
import IotIcon from '@assets/svgs/iotIconNew.svg'
+import HntIcon from '@assets/svgs/hnt.svg'
import TouchableContainer from '@components/TouchableContainer'
import BalanceText from '@components/BalanceText'
import useHotspots from '@hooks/useHotspots'
@@ -15,6 +16,7 @@ import { toNumber } from '@helium/spl-utils'
import {
MOBILE_LAZY_KEY,
IOT_LAZY_KEY,
+ HNT_LAZY_KEY,
MIN_BALANCE_THRESHOLD,
} from '@utils/constants'
import useSubmitTxn from '@hooks/useSubmitTxn'
@@ -44,6 +46,7 @@ const ClaimTokensPage = () => {
const {
pendingIotRewards,
+ pendingHntRewards,
pendingMobileRewards,
hotspotsWithMeta,
totalHotspots,
@@ -62,6 +65,11 @@ const ClaimTokensPage = () => {
return toNumber(pendingIotRewards, 6)
}, [pendingIotRewards])
+ const totalPendingHnt = useMemo(() => {
+ if (!pendingHntRewards) return 0
+ return toNumber(pendingHntRewards, 6)
+ }, [pendingHntRewards])
+
const totalPendingMobile = useMemo(() => {
if (!pendingMobileRewards) return 0
return toNumber(pendingMobileRewards, 6)
@@ -83,15 +91,23 @@ const ClaimTokensPage = () => {
return (
claiming ||
!hasEnoughSol ||
- (totalPendingIot === 0 && totalPendingMobile === 0)
+ (totalPendingIot === 0 &&
+ totalPendingMobile === 0 &&
+ totalPendingHnt === 0)
)
- }, [claiming, hasEnoughSol, totalPendingIot, totalPendingMobile])
+ }, [
+ claiming,
+ hasEnoughSol,
+ totalPendingIot,
+ totalPendingMobile,
+ totalPendingHnt,
+ ])
const onClaim = useCallback(async () => {
try {
const claim = async () => {
await submitClaimAllRewards(
- [IOT_LAZY_KEY, MOBILE_LAZY_KEY],
+ [IOT_LAZY_KEY, MOBILE_LAZY_KEY, HNT_LAZY_KEY],
hotspotsWithMeta,
totalHotspots,
)
@@ -154,25 +170,48 @@ const ClaimTokensPage = () => {
gap="1"
marginTop="4xl"
>
-
-
-
-
-
-
- MOBILE
-
+ {totalPendingMobile > 0 && (
+
+
+
+
+
+
+ MOBILE
+
+
-
-
+
+ )}
+ {totalPendingIot > 0 && (
+
+
+
+
+
+
+ IOT
+
+
+
+
+ )}
{
}}
>
-
+
-
+
- IOT
+ HNT
diff --git a/src/features/hotspots/ChangeRewardsRecipientScreen.tsx b/src/features/hotspots/ChangeRewardsRecipientScreen.tsx
index a2bcac50..7d83bca0 100644
--- a/src/features/hotspots/ChangeRewardsRecipientScreen.tsx
+++ b/src/features/hotspots/ChangeRewardsRecipientScreen.tsx
@@ -26,7 +26,7 @@ import {
TouchableWithoutFeedback,
} from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
-import { IOT_LAZY_KEY, MOBILE_LAZY_KEY } from '@utils/constants'
+import { HNT_LAZY_KEY, IOT_LAZY_KEY, MOBILE_LAZY_KEY } from '@utils/constants'
import { PublicKey } from '@solana/web3.js'
import TouchableOpacityBox from '@components/TouchableOpacityBox'
import { useCurrentWallet } from '@hooks/useCurrentWallet'
@@ -139,7 +139,7 @@ const ChangeRewardsRecipientScreen = () => {
setUpdating(true)
try {
await submitUpdateRewardsDestination({
- lazyDistributors: [IOT_LAZY_KEY, MOBILE_LAZY_KEY],
+ lazyDistributors: [IOT_LAZY_KEY, MOBILE_LAZY_KEY, HNT_LAZY_KEY],
destination: recipient,
assetId: hotspot.id,
})
@@ -157,7 +157,7 @@ const ChangeRewardsRecipientScreen = () => {
setRemoving(true)
try {
await submitUpdateRewardsDestination({
- lazyDistributors: [IOT_LAZY_KEY, MOBILE_LAZY_KEY],
+ lazyDistributors: [IOT_LAZY_KEY, MOBILE_LAZY_KEY, HNT_LAZY_KEY],
destination: PublicKey.default.toBase58(),
assetId: hotspot.id,
})
diff --git a/src/features/hotspots/ClaimAllRewardsScreen.tsx b/src/features/hotspots/ClaimAllRewardsScreen.tsx
index 78bc01c5..51a51be0 100644
--- a/src/features/hotspots/ClaimAllRewardsScreen.tsx
+++ b/src/features/hotspots/ClaimAllRewardsScreen.tsx
@@ -15,6 +15,7 @@ import useSubmitTxn from '@hooks/useSubmitTxn'
import { useNavigation } from '@react-navigation/native'
import { useModal } from '@config/storage/ModalsProvider'
import {
+ HNT_LAZY_KEY,
IOT_LAZY_KEY,
MIN_BALANCE_THRESHOLD,
MOBILE_LAZY_KEY,
@@ -61,7 +62,7 @@ const ClaimAllRewardsScreen = () => {
setRedeeming(true)
const claim = async () => {
await submitClaimAllRewards(
- [IOT_LAZY_KEY, MOBILE_LAZY_KEY],
+ [IOT_LAZY_KEY, MOBILE_LAZY_KEY, HNT_LAZY_KEY],
hotspotsWithMeta,
totalHotspots,
)
diff --git a/src/features/hotspots/ClaimRewardsScreen.tsx b/src/features/hotspots/ClaimRewardsScreen.tsx
index 046ac840..20b58ce9 100644
--- a/src/features/hotspots/ClaimRewardsScreen.tsx
+++ b/src/features/hotspots/ClaimRewardsScreen.tsx
@@ -33,7 +33,8 @@ const ClaimRewardsScreen = () => {
const mint = useMemo(() => new PublicKey(hotspot.id), [hotspot.id])
const { submitClaimRewards } = useSubmitTxn()
- const { createClaimMobileTx, createClaimIotTx } = useHotspot(mint)
+ const { createClaimMobileTx, createClaimIotTx, createClaimHntTx } =
+ useHotspot(mint)
const pendingIotRewards = useMemo(
() =>
@@ -51,6 +52,14 @@ const ClaimRewardsScreen = () => {
[hotspot],
)
+ const pendingHntRewards = useMemo(
+ () =>
+ hotspot &&
+ hotspot.pendingRewards &&
+ new BN(hotspot.pendingRewards[Mints.HNT]),
+ [hotspot],
+ )
+
const title = useMemo(() => {
return t('collectablesScreen.hotspots.claimRewards')
}, [t])
@@ -73,6 +82,11 @@ const ClaimRewardsScreen = () => {
pendingMobileRewards && !pendingMobileRewards.eq(new BN(0))
? await createClaimMobileTx()
: undefined
+ const claimHntTx =
+ pendingHntRewards && !pendingHntRewards.eq(new BN(0))
+ ? await createClaimHntTx()
+ : undefined
+
const transactions: VersionedTransaction[] = []
if (claimIotTx && pendingIotRewards) {
@@ -83,6 +97,10 @@ const ClaimRewardsScreen = () => {
transactions.push(claimMobileTx)
}
+ if (claimHntTx && pendingHntRewards) {
+ transactions.push(claimHntTx)
+ }
+
if (transactions.length > 0) {
await submitClaimRewards(transactions)
nav.push('ClaimingRewardsScreen')
diff --git a/src/features/hotspots/HotspotMapHotspotDetails.tsx b/src/features/hotspots/HotspotMapHotspotDetails.tsx
index a7227059..9f63d802 100644
--- a/src/features/hotspots/HotspotMapHotspotDetails.tsx
+++ b/src/features/hotspots/HotspotMapHotspotDetails.tsx
@@ -1,6 +1,7 @@
import CopyAddress from '@assets/svgs/copyAddress.svg'
import Hex from '@assets/svgs/hex.svg'
import IotSymbol from '@assets/svgs/iotSymbol.svg'
+import HntSymbol from '@assets/svgs/hntSymbol.svg'
import MobileSymbol from '@assets/svgs/mobileSymbol.svg'
import { ReAnimatedBlurBox } from '@components/AnimatedBox'
import Box from '@components/Box'
@@ -13,7 +14,7 @@ import TouchableOpacityBox from '@components/TouchableOpacityBox'
import { makerApprovalKey } from '@helium/helium-entity-manager-sdk'
import { useMint } from '@helium/helium-react-hooks'
import { NetworkType } from '@helium/onboarding'
-import { IOT_MINT, MOBILE_MINT, toNumber } from '@helium/spl-utils'
+import { HNT_MINT, IOT_MINT, MOBILE_MINT, toNumber } from '@helium/spl-utils'
import useCopyText from '@hooks/useCopyText'
import { useCurrentWallet } from '@hooks/useCurrentWallet'
import { useEntityKey } from '@hooks/useEntityKey'
@@ -161,6 +162,7 @@ export const HotspotMapHotspotDetails = ({
const streetAddress = useHotspotAddress(hotspotWithMeta)
const { info: iotMint } = useMint(IOT_MINT)
const { info: mobileMint } = useMint(MOBILE_MINT)
+ const { info: hntMint } = useMint(HNT_MINT)
// Use entity key from kta since it's a buffer
const iotInfoAcc = useIotInfo(kta?.entityKey)
const mobileInfoAcc = useMobileInfo(kta?.entityKey)
@@ -239,6 +241,21 @@ export const HotspotMapHotspotDetails = ({
return formatLargeNumber(new BigNumber(num))
}, [hotspotWithMeta, mobileMint])
+ const pendingHntRewards = useMemo(
+ () =>
+ hotspotWithMeta?.pendingRewards &&
+ new BN(hotspotWithMeta?.pendingRewards[Mints.HNT]),
+ [hotspotWithMeta],
+ )
+ const pendingHntRewardsString = useMemo(() => {
+ if (!hotspotWithMeta?.pendingRewards) return
+ const num = toNumber(
+ new BN(hotspotWithMeta?.pendingRewards[Mints.HNT]),
+ hntMint?.decimals || 6,
+ )
+ return formatLargeNumber(new BigNumber(num))
+ }, [hotspotWithMeta, hntMint])
+
const hasIotRewards = useMemo(
() => pendingIotRewards && pendingIotRewards.gt(new BN(0)),
[pendingIotRewards],
@@ -247,10 +264,14 @@ export const HotspotMapHotspotDetails = ({
() => pendingMobileRewards && pendingMobileRewards.gt(new BN(0)),
[pendingMobileRewards],
)
+ const hasHntRewards = useMemo(
+ () => pendingHntRewards && pendingHntRewards.gt(new BN(0)),
+ [pendingHntRewards],
+ )
const hasRewards = useMemo(
- () => hasIotRewards || hasMobileRewards,
- [hasIotRewards, hasMobileRewards],
+ () => hasIotRewards || hasMobileRewards || hasHntRewards,
+ [hasIotRewards, hasMobileRewards, hasHntRewards],
)
const mobileRecipient = useMemo(
@@ -263,6 +284,11 @@ export const HotspotMapHotspotDetails = ({
[hotspotWithMeta],
)
+ const hntRecipient = useMemo(
+ () => hotspotWithMeta?.rewardRecipients?.[Mints.HNT],
+ [hotspotWithMeta],
+ )
+
const hasIotRecipient = useMemo(
() =>
iotRecipient?.destination &&
@@ -272,6 +298,15 @@ export const HotspotMapHotspotDetails = ({
[iotRecipient, wallet],
)
+ const hasHntRecipient = useMemo(
+ () =>
+ hntRecipient?.destination &&
+ wallet &&
+ !new PublicKey(hntRecipient.destination).equals(wallet) &&
+ !new PublicKey(hntRecipient.destination).equals(PublicKey.default),
+ [hntRecipient, wallet],
+ )
+
const hasMobileRecipient = useMemo(
() =>
mobileRecipient?.destination &&
@@ -282,8 +317,8 @@ export const HotspotMapHotspotDetails = ({
)
const hasRecipientSet = useMemo(
- () => hasIotRecipient || hasMobileRecipient,
- [hasIotRecipient, hasMobileRecipient],
+ () => hasIotRecipient || hasMobileRecipient || hasHntRecipient,
+ [hasIotRecipient, hasMobileRecipient, hasHntRecipient],
)
const isLoading = useMemo(
@@ -686,6 +721,31 @@ export const HotspotMapHotspotDetails = ({
)}
+ {!!hasHntRewards && (
+
+
+
+ {pendingHntRewardsString}
+
+
+ )}
diff --git a/src/features/hotspots/HotspotPage.tsx b/src/features/hotspots/HotspotPage.tsx
index b8d4bbe9..c2f2294c 100644
--- a/src/features/hotspots/HotspotPage.tsx
+++ b/src/features/hotspots/HotspotPage.tsx
@@ -17,7 +17,7 @@ import { Location, MarkerView } from '@rnmapbox/maps'
import ImageBox from '@components/ImageBox'
import CarotRight from '@assets/svgs/carot-right.svg'
import { getDistance } from 'geolib'
-import { MOBILE_MINT, toNumber as heliumToNumber } from '@helium/spl-utils'
+import { HNT_MINT, MOBILE_MINT, toNumber as heliumToNumber } from '@helium/spl-utils'
import { BN } from '@coral-xyz/anchor'
import { toNumber } from 'lodash'
import MiniMap from '@components/MiniMap'
@@ -134,11 +134,11 @@ const HotspotPage = () => {
}
return (
(heliumToNumber(
- new BN(b?.pendingRewards?.[MOBILE_MINT?.toBase58()] ?? 0),
+ new BN(b?.pendingRewards?.[HNT_MINT?.toBase58()] ?? 0),
6,
) ?? 0) -
(heliumToNumber(
- new BN(a?.pendingRewards?.[MOBILE_MINT?.toBase58()] ?? 0),
+ new BN(a?.pendingRewards?.[HNT_MINT?.toBase58()] ?? 0),
6,
) ?? 0)
)
diff --git a/src/hooks/useHotspot.tsx b/src/hooks/useHotspot.tsx
index 6491fe2b..e5e627e1 100644
--- a/src/hooks/useHotspot.tsx
+++ b/src/hooks/useHotspot.tsx
@@ -4,7 +4,7 @@ import { PublicKey, VersionedTransaction } from '@solana/web3.js'
import { useEffect, useState } from 'react'
import { useAsyncCallback } from 'react-async-hook'
import { useSolana } from '@features/solana/SolanaProvider'
-import { IOT_LAZY_KEY, MOBILE_LAZY_KEY } from '../utils/constants'
+import { HNT_LAZY_KEY, IOT_LAZY_KEY, MOBILE_LAZY_KEY } from '../utils/constants'
import * as Logger from '../utils/logger'
export function useHotspot(mint: PublicKey): {
@@ -12,8 +12,11 @@ export function useHotspot(mint: PublicKey): {
mobileRewardsError: Error | undefined
createClaimIotTx: () => Promise
createClaimMobileTx: () => Promise
+ createClaimHntTx: () => Promise
iotRewardsLoading: boolean
mobileRewardsLoading: boolean
+ hntRewardsLoading: boolean
+ hntRewardsError: Error | undefined
} {
const { anchorProvider: provider } = useSolana()
@@ -85,6 +88,39 @@ export function useHotspot(mint: PublicKey): {
}
})
+ const {
+ error: hntRewardsError,
+ execute: createClaimHntTx,
+ loading: hntRewardsLoading,
+ } = useAsyncCallback(async () => {
+ if (!provider) return
+ const program = await init(provider)
+ const { connection } = provider
+
+ if (mint && program && provider) {
+ const rewards = await client.getCurrentRewards(
+ // TODO: Fix program type once HPL is upgraded to anchor v0.26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ program,
+ HNT_LAZY_KEY,
+ mint,
+ )
+
+ const tx = await client.formTransaction({
+ // TODO: Fix program type once HPL is upgraded to anchor v0.26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ program,
+ provider,
+ rewards,
+ hotspot: mint,
+ lazyDistributor: HNT_LAZY_KEY,
+ assetEndpoint: connection.rpcEndpoint,
+ })
+
+ return tx
+ }
+ })
+
useEffect(() => {
if (mobileRewardsError) {
Logger.error(mobileRewardsError)
@@ -95,9 +131,12 @@ export function useHotspot(mint: PublicKey): {
return {
createClaimMobileTx,
createClaimIotTx,
+ createClaimHntTx,
mobileRewardsLoading,
iotRewardsLoading,
+ hntRewardsLoading,
iotRewardsError,
mobileRewardsError,
+ hntRewardsError,
}
}
diff --git a/src/hooks/useHotspots.ts b/src/hooks/useHotspots.ts
index c606a4ac..4e95c871 100644
--- a/src/hooks/useHotspots.ts
+++ b/src/hooks/useHotspots.ts
@@ -16,6 +16,7 @@ import { Mints } from '../utils/constants'
const useHotspots = (): {
totalHotspots: number | undefined
pendingIotRewards: BN | undefined
+ pendingHntRewards: BN | undefined
pendingMobileRewards: BN | undefined
hotspots: CompressedNFT[]
hotspotsWithMeta: HotspotWithPendingRewards[]
@@ -191,6 +192,18 @@ const useHotspots = (): {
[hotspotsWithMeta],
)
+ const pendingHntRewards = useMemo(
+ () =>
+ hotspotsWithMeta?.reduce((acc, hotspot) => {
+ if (hotspot.pendingRewards) {
+ return acc.add(new BN(hotspot.pendingRewards[Mints.HNT] || '0'))
+ }
+
+ return acc
+ }, new BN(0)),
+ [hotspotsWithMeta],
+ )
+
const pendingMobileRewards = useMemo(
() =>
hotspotsWithMeta?.reduce((acc, hotspot) => {
@@ -210,6 +223,7 @@ const useHotspots = (): {
return {
totalHotspots,
pendingIotRewards,
+ pendingHntRewards,
pendingMobileRewards,
loading: false,
hotspots: [],
@@ -225,6 +239,7 @@ const useHotspots = (): {
return {
totalHotspots,
pendingIotRewards,
+ pendingHntRewards,
pendingMobileRewards,
hotspots,
hotspotsWithMeta,
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 2c7b46c9..ddf5d424 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -26,6 +26,7 @@ export const MOBILE_LAZY_KEY = lazyDistributorKey(
)[0]
export const IOT_LAZY_KEY = lazyDistributorKey(new PublicKey(Mints.IOT))[0]
+export const HNT_LAZY_KEY = lazyDistributorKey(new PublicKey(Mints.HNT))[0]
export const DAO_KEY = daoKey(HNT_MINT)[0]
export const IOT_SUB_DAO_KEY = subDaoKey(IOT_MINT)[0]
export const MOBILE_SUB_DAO_KEY = subDaoKey(MOBILE_MINT)[0]
diff --git a/src/utils/solanaUtils.ts b/src/utils/solanaUtils.ts
index 88cb94a3..df88f47b 100644
--- a/src/utils/solanaUtils.ts
+++ b/src/utils/solanaUtils.ts
@@ -127,7 +127,7 @@ import {
} from '../types/solana'
import { WrappedConnection } from './WrappedConnection'
import { solAddressIsValid } from './accountUtils'
-import { DAO_KEY, IOT_LAZY_KEY, MOBILE_LAZY_KEY, Mints } from './constants'
+import { DAO_KEY, HNT_LAZY_KEY, IOT_LAZY_KEY, MOBILE_LAZY_KEY, Mints } from './constants'
import { decimalSeparator, groupSeparator } from './i18n'
import * as Logger from './logger'
import sleep from './sleep'
@@ -1050,6 +1050,14 @@ export const getHotspotPendingRewards = async (
'b58',
true,
)
+ const hntRewards = await getPendingRewards(
+ program,
+ HNT_LAZY_KEY,
+ dao,
+ entityKeys,
+ 'b58',
+ true,
+ )
return hotspots.map((hotspot, index) => {
const entityKey = entityKeys[index]
@@ -1059,6 +1067,7 @@ export const getHotspotPendingRewards = async (
pendingRewards: {
[Mints.MOBILE]: mobileRewards[entityKey],
[Mints.IOT]: iotRewards[entityKey],
+ [Mints.HNT]: hntRewards[entityKey],
},
}
})
@@ -1082,6 +1091,7 @@ export const getHotspotRecipients = async (
(acc: PublicKey[][], asset) => [
[...(acc[0] || []), recipientKey(MOBILE_LAZY_KEY, asset)[0]],
[...(acc[1] || []), recipientKey(IOT_LAZY_KEY, asset)[0]],
+ [...(acc[2] || []), recipientKey(HNT_LAZY_KEY, asset)[0]],
],
[],
)
@@ -1313,6 +1323,15 @@ export async function annotateWithPendingRewards(
true,
)
+ const hntRewards = await getPendingRewards(
+ program,
+ HNT_LAZY_KEY,
+ dao,
+ entityKeys,
+ 'b58',
+ true,
+ )
+
const rewardRecipients = await getHotspotRecipients(provider, hotspots)
const rewardRecipientsById: {
[key: string]: { [key: string]: RecipientV0 }
@@ -1332,6 +1351,7 @@ export async function annotateWithPendingRewards(
pendingRewards: {
[Mints.MOBILE]: mobileRewards[entityKey],
[Mints.IOT]: iotRewards[entityKey],
+ [Mints.HNT]: hntRewards[entityKey],
},
rewardRecipients: rewardRecipientsById[hotspot.id] || {},
} as HotspotWithPendingRewards