Skip to content

Commit 84ec702

Browse files
committed
feat: Implement watchBalance & update useBalance with optional subscribing
1 parent d3f338f commit 84ec702

File tree

2 files changed

+87
-39
lines changed

2 files changed

+87
-39
lines changed

src/helpers/getBalance.ts

+58-16
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,77 @@ import { ApiPromise } from '@polkadot/api'
22
import { AccountId } from '@polkadot/types/interfaces'
33
import { BN, formatBalance } from '@polkadot/util'
44

5-
/**
6-
* Returns the native token balance of the given `address`.
7-
*/
8-
export const getBalance = async (
9-
api: ApiPromise,
10-
address: string | AccountId | undefined,
11-
): Promise<{
5+
export type BalanceData = {
126
tokenDecimals: number
137
tokenSymbol: string
148
freeBalance?: BN
159
reservedBalance?: BN
1610
balance?: BN
1711
balanceFormatted?: string
18-
}> => {
19-
// Get the token decimals and symbol
20-
const tokenDecimals = api.registry.chainDecimals?.[0] || 12
21-
const tokenSymbol = api.registry.chainTokens?.[0] || 'Unit'
12+
}
2213

14+
/**
15+
* Returns the native token balance of the given `address`.
16+
*/
17+
export const getBalance = async (
18+
api: ApiPromise,
19+
address: string | AccountId | undefined,
20+
): Promise<BalanceData> => {
21+
const { tokenDecimals, tokenSymbol } = parseBalanceData(api)
2322
if (!address) {
2423
return {
2524
tokenDecimals,
2625
tokenSymbol,
2726
}
2827
}
2928

30-
// Get the balance
29+
// Query the chain and parse data
3130
const result: any = await api.query.system.account(address)
32-
const freeBalance: BN = new BN(result?.data?.free || 0)
33-
const reservedBalance: BN = new BN(result?.data?.reserved || 0)
31+
const balanceData = parseBalanceData(api, result?.data)
32+
33+
return balanceData
34+
}
35+
36+
/**
37+
* Watches the native token balance of the given `address` and returns it in a callback.
38+
* The returned void function can be used to unsubscribe.
39+
*/
40+
export const watchBalance = async (
41+
api: ApiPromise,
42+
address: string | AccountId | undefined,
43+
callback: (data: BalanceData) => void,
44+
): Promise<VoidFunction | null> => {
45+
const { tokenDecimals, tokenSymbol } = parseBalanceData(api)
46+
if (!address) {
47+
callback({
48+
tokenDecimals,
49+
tokenSymbol,
50+
})
51+
return null
52+
}
53+
54+
// Query the chain, parse data, and call the callback
55+
const unsubscribe: any = await api.query.system.account(
56+
address,
57+
({ data }: any) => {
58+
const balanceData = parseBalanceData(api, data)
59+
callback(balanceData)
60+
},
61+
)
62+
return unsubscribe
63+
}
64+
65+
/**
66+
* Helper to parse the fetched balance data.
67+
*/
68+
const parseBalanceData = (api: ApiPromise, data?: any): BalanceData => {
69+
// Get the token decimals and symbol
70+
const tokenDecimals = api.registry.chainDecimals?.[0] || 12
71+
const tokenSymbol = api.registry.chainTokens?.[0] || 'Unit'
72+
73+
// Get the balance
74+
const freeBalance: BN = new BN(data?.free || 0)
75+
const reservedBalance: BN = new BN(data?.reserved || 0)
3476
const balance = reservedBalance.add(freeBalance)
3577

3678
// Format the balance
@@ -39,11 +81,11 @@ export const getBalance = async (
3981
})
4082

4183
return {
84+
tokenDecimals,
85+
tokenSymbol,
4286
freeBalance,
4387
reservedBalance,
4488
balance,
4589
balanceFormatted,
46-
tokenSymbol,
47-
tokenDecimals,
4890
}
4991
}

src/hooks/useBalance.ts

+29-23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getBalance } from '@helpers/getBalance'
1+
import { BalanceData, getBalance, watchBalance } from '@helpers/getBalance'
22
import { AccountId } from '@polkadot/types/interfaces'
33
import { BN } from '@polkadot/util'
44
import { useInkathon } from '@provider'
@@ -7,39 +7,45 @@ import { useEffect, useState } from 'react'
77
/**
88
* Hook that returns the native token balance of the given `address`.
99
*/
10-
export const useBalance = (address?: string | AccountId) => {
10+
export const useBalance = (address?: string | AccountId, watch?: boolean) => {
1111
const { api } = useInkathon()
1212
const [freeBalance, setFreeBalance] = useState<BN>()
1313
const [reservedBalance, setReservedBalance] = useState<BN>()
1414
const [balance, setBalance] = useState<BN>()
1515
const [balanceFormatted, setBalanceFormatted] = useState<string>()
1616
const [tokenSymbol, setTokenSymbol] = useState<string>()
1717
const [tokenDecimals, setTokenDecimals] = useState<number>()
18+
const [unsubscribes, setUnsubscribes] = useState<(VoidFunction | null)[]>([])
1819

1920
useEffect(() => {
20-
;(async () => {
21-
if (!api) {
22-
setFreeBalance(undefined)
23-
setReservedBalance(undefined)
24-
setBalance(undefined)
25-
setBalanceFormatted(undefined)
26-
setTokenSymbol(undefined)
27-
setTokenDecimals(undefined)
28-
return
29-
}
30-
31-
const result = await getBalance(api, address)
32-
33-
setFreeBalance(result.freeBalance)
34-
setReservedBalance(result.reservedBalance)
35-
setBalance(result.balance)
21+
const updateBalanceData = (data: BalanceData) => {
22+
setFreeBalance(data.freeBalance)
23+
setReservedBalance(data.reservedBalance)
24+
setBalance(data.balance)
3625
setBalanceFormatted(
37-
result.balanceFormatted &&
38-
`${result.balanceFormatted} ${result.tokenSymbol}`,
26+
data.balanceFormatted && `${data.balanceFormatted} ${data.tokenSymbol}`,
3927
)
40-
setTokenSymbol(result.tokenSymbol)
41-
setTokenDecimals(result.tokenDecimals)
42-
})()
28+
setTokenSymbol(data.tokenSymbol)
29+
setTokenDecimals(data.tokenDecimals)
30+
}
31+
32+
if (!api) {
33+
updateBalanceData({} as BalanceData)
34+
return
35+
}
36+
37+
if (watch) {
38+
watchBalance(api, address, updateBalanceData).then((unsubscribe) => {
39+
setUnsubscribes((prev) => [...prev, unsubscribe])
40+
})
41+
} else {
42+
getBalance(api, address).then(updateBalanceData)
43+
}
44+
45+
return () => {
46+
unsubscribes.forEach((unsubscribe) => unsubscribe?.())
47+
setUnsubscribes(() => [])
48+
}
4349
}, [api, address])
4450

4551
return {

0 commit comments

Comments
 (0)