Total Balance
-
{getBalanceAmount(new BigNumber(accountData.balance), token?.decimals).toFormat()}
+
+ {formatBalanceAmount(new BigNumber(accountData.balance), token?.decimals)}
+ {tokenSymbol}
+
Transferrable
-
{getBalanceAmount(transferable, token?.decimals).toFormat()}
+
+ {formatBalanceAmount(transferable, token?.decimals)}
+ {tokenSymbol}
+
Locked
-
{getBalanceAmount(new BigNumber(accountData.locked), token?.decimals).toFormat()}
+
+ {formatBalanceAmount(new BigNumber(accountData.locked), token?.decimals)}
+ {tokenSymbol}
+
Vested
-
{getBalanceAmount(new BigNumber(accountData.vested || 0), token?.decimals).toFormat()}
+
+ {formatBalanceAmount(new BigNumber(accountData.vested || 0), token?.decimals)}
+ {tokenSymbol}
+
Reserved
-
{getBalanceAmount(new BigNumber(accountData.reserved), token?.decimals).toFormat()}
+
+ {formatBalanceAmount(new BigNumber(accountData.reserved), token?.decimals)}
+ {tokenSymbol}
+
diff --git a/ui-react/src/pages/token/[id].tsx b/ui-react/src/pages/token/[id].tsx
index 1934560..2c6ef27 100644
--- a/ui-react/src/pages/token/[id].tsx
+++ b/ui-react/src/pages/token/[id].tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { CardBody, Card, Tabs, Tab, Divider } from '@heroui/react'
import { useRouter } from 'next/router'
-import { getBalanceAmount, getThemeColor } from '@/utils/text'
+import { formatBalanceAmount, getThemeColor } from '@/utils/text'
import { unwrap, usePVMTokens } from '@/utils/api'
import { useData } from '@/context'
import BigNumber from 'bignumber.js'
@@ -53,7 +53,7 @@ export default function Page() {
Token Supply
-
{getBalanceAmount(new BigNumber(tokenData.totalSupply), tokenData.decimals).toFormat()}
+
{formatBalanceAmount(new BigNumber(tokenData.totalSupply), tokenData.decimals)}
diff --git a/ui-react/src/pages/tx/[id].tsx b/ui-react/src/pages/tx/[id].tsx
index b203189..365c788 100644
--- a/ui-react/src/pages/tx/[id].tsx
+++ b/ui-react/src/pages/tx/[id].tsx
@@ -1,7 +1,7 @@
import React, { useMemo } from 'react'
import { CardBody, Card, Divider } from '@heroui/react'
import { useRouter } from 'next/router'
-import { getBalanceAmount, getUTCTime, timeAgo } from '@/utils/text'
+import { formatBalanceAmount, getUTCTime, timeAgo } from '@/utils/text'
import { unwrap, usePVMTx } from '@/utils/api'
import { Container, PageContent } from '@/ui'
import { PVM_DECIMAL } from '@/utils/const'
@@ -87,7 +87,7 @@ export default function Page() {
Value
-
{getBalanceAmount(new BigNumber(extrinsicData.value), PVM_DECIMAL).toFormat()}
+
{formatBalanceAmount(new BigNumber(extrinsicData.value), PVM_DECIMAL)}
@@ -110,12 +110,12 @@ export default function Page() {
Txn Fee
- {getBalanceAmount(
+ {formatBalanceAmount(
new BigNumber(extrinsicData.gas_used).times(
extrinsicData.txn_type === 2 ? extrinsicData.effective_gas_price : extrinsicData.gas_price
),
PVM_DECIMAL
- ).toFormat()}
+ )}
diff --git a/ui-react/src/utils/api.ts b/ui-react/src/utils/api.ts
index 29c5d06..dfdd1ea 100644
--- a/ui-react/src/utils/api.ts
+++ b/ui-react/src/utils/api.ts
@@ -28,6 +28,14 @@ const runtimeFetcher = ([host, url, data]: [string, string, any]) => {
return axiosInstance.post((host || API_HOST) + url, data).then((res) => res.data)
}
+export type searchHashType = {
+ hash_type: string
+}
+
+export async function checkSearchHash(host: string, data: { hash: string }): Promise
> {
+ return runtimeFetcher([host, '/api/scan/check_hash', data])
+}
+
// const postFetcher = ([url, data]: [string, any]) => {
// return axiosInstance.post('/api/proxy', {
// path: url,
@@ -219,10 +227,14 @@ export type transferType = {
amount: string
blockNum: number
block_timestamp: number
+ balance_event?: string
+ category?: string
extrinsic_index: string
id: number
receiver: string
sender: string
+ source_event?: string
+ source_module?: string
symbol: string
token_id: string
}
@@ -450,8 +462,8 @@ export type pvmAccountListType = {
list: pvmAccountType[] | null
count: number
pagination: {
- start_cursor: number,
- end_cursor: number,
+ start_cursor: string,
+ end_cursor: string,
has_next_page: boolean,
has_previous_page: boolean
}
@@ -460,9 +472,10 @@ export type pvmAccountListType = {
export type getPVMAccountListParams = {
page?: number
row?: number
- after?: number
- before?: number
+ after?: string
+ before?: string
address?: string
+ include_contracts?: boolean
}
export const usePVMAccounts = (host: string, data: getPVMAccountListParams) => {
@@ -650,6 +663,7 @@ export type getPVMContractListParams = {
row?: number
after?: number
before?: number
+ verified_source?: boolean
}
type getPVMContractParams = {
diff --git a/ui-react/src/utils/text.ts b/ui-react/src/utils/text.ts
index 1fd1ac5..13f4674 100644
--- a/ui-react/src/utils/text.ts
+++ b/ui-react/src/utils/text.ts
@@ -47,6 +47,36 @@ export function getBalanceAmount(amount: BigNumber, decimals?: number): BigNumbe
return new BigNumber(amount).dividedBy(BIG_TEN.pow(decimals || 0))
}
+const COMPACT_BALANCE_UNITS = [
+ { value: new BigNumber('1000000000000'), label: 'Trillion' },
+ { value: new BigNumber('1000000000'), label: 'Billion' },
+ { value: new BigNumber('1000000'), label: 'Million' },
+] as const
+
+export function formatBalanceAmount(amount: BigNumber, decimals?: number): string {
+ const balance = getBalanceAmount(amount, decimals)
+ const absBalance = balance.abs()
+ const compactUnit = COMPACT_BALANCE_UNITS.find((unit) => absBalance.isGreaterThanOrEqualTo(unit.value))
+
+ if (compactUnit) {
+ return `${balance.dividedBy(compactUnit.value).decimalPlaces(4, BigNumber.ROUND_HALF_UP).toFormat(4)} ${compactUnit.label}`
+ }
+
+ if (absBalance.isZero()) {
+ return '0'
+ }
+
+ if (absBalance.isLessThan(1)) {
+ if (absBalance.isLessThan('0.00000001')) {
+ return balance.isNegative() ? '> -0.00000001' : '< 0.00000001'
+ }
+
+ return balance.decimalPlaces(8, BigNumber.ROUND_HALF_UP).toFormat()
+ }
+
+ return balance.decimalPlaces(6, BigNumber.ROUND_HALF_UP).toFormat()
+}
+
export function timeAgo(time: number | string, now = Date.now()) {
const second = +time * 1000
const d = new Date(second)
diff --git a/util/address/address.go b/util/address/address.go
index 7c4c476..9edac6b 100644
--- a/util/address/address.go
+++ b/util/address/address.go
@@ -10,6 +10,13 @@ import (
var (
ethAddressRegex = regexp.MustCompile(`^0x[0-9a-fA-F]{40}$`)
substrateAddressRegex = regexp.MustCompile(`^[0-9a-fA-F]{64}$`)
+ hashRegex = regexp.MustCompile(`^0x[0-9a-fA-F]{64}$`)
+)
+
+const (
+ SearchTypeHash = "hash"
+ SearchTypeEvmAddress = "evm_address"
+ SearchTypeSubstrateAddress = "address"
)
// SS58Address converts the address to SS58 format
@@ -35,6 +42,21 @@ func VerifySubstrateAddress(accountId string) bool {
return substrateAddressRegex.MatchString(util.TrimHex(accountId))
}
+// DetectSearchType returns the obvious search target type without touching storage.
+func DetectSearchType(search string) string {
+ search = strings.TrimSpace(search)
+ switch {
+ case VerifyEthereumAddress(search):
+ return SearchTypeEvmAddress
+ case hashRegex.MatchString(search):
+ return SearchTypeHash
+ case VerifySubstrateAddress(search), ss58.Decode(search) != "":
+ return SearchTypeSubstrateAddress
+ default:
+ return ""
+ }
+}
+
// Decode converts the address to Substrate public key or Ethereum format, depending on the address type Ethereum or Substrate
func Decode(address string) string {
if VerifyEthereumAddress(address) {
diff --git a/util/address/address_test.go b/util/address/address_test.go
index ec96dc0..20382ab 100644
--- a/util/address/address_test.go
+++ b/util/address/address_test.go
@@ -53,3 +53,12 @@ func TestFormat(t *testing.T) {
assert.Equal(t, Format("0x3a370c6e4af506123c30e091a1cbfbc3728e1ec5"), "0x3a370c6e4af506123c30e091a1cbfbc3728e1ec5")
assert.Equal(t, Format("3a370c6e4af506123c30e091a1cbfbc3728e1ec5"), "0x3a370c6e4af506123c30e091a1cbfbc3728e1ec5")
}
+
+func TestDetectSearchType(t *testing.T) {
+ assert.Equal(t, SearchTypeHash, DetectSearchType("0xbadc6963e1add4d7a588e350d837579491d08bb270f02c56b3dd5f17018dee0c"))
+ assert.Equal(t, SearchTypeEvmAddress, DetectSearchType("0x3a370c6e4af506123c30e091a1cbfbc3728e1ec5"))
+ assert.Equal(t, SearchTypeEvmAddress, DetectSearchType("3a370c6e4af506123c30e091a1cbfbc3728e1ec5"))
+ assert.Equal(t, SearchTypeSubstrateAddress, DetectSearchType("12KL8YptX9SuUCZGrsNrSRzp3zHNqbwLqmfN8vubtj1z1Bqv"))
+ assert.Equal(t, SearchTypeSubstrateAddress, DetectSearchType("3a370c6e4af506123c30e091a1cbfbc3728e1ec5fc47d87457fbb0b504903260"))
+ assert.Equal(t, "", DetectSearchType("not-an-address"))
+}