Skip to content

Commit

Permalink
feat: scale-ts
Browse files Browse the repository at this point in the history
  • Loading branch information
alecdwm committed Jul 3, 2024
1 parent 7672a73 commit e02ec54
Show file tree
Hide file tree
Showing 83 changed files with 1,681 additions and 4,248 deletions.
29 changes: 27 additions & 2 deletions apps/balances-demo/src/components/Balances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@ export const Balances = () => {
const balances = useBalances()

return (
<div className="grid grid-cols-[repeat(6,_auto)] items-center gap-4">
<div className="grid grid-cols-[repeat(7,_auto)] items-center gap-4">
<>
<div className="text-tiny justify-self-center font-bold">Logo</div>
<div className="text-tiny font-bold">Colour</div>
<div className="text-tiny justify-self-center font-bold">Status</div>
<div className="text-tiny font-bold">Chain</div>
<div className="text-tiny font-bold">Total</div>
<div className="text-tiny font-bold">Available</div>
<div className="text-tiny font-bold">Account</div>
</>
{balances?.sorted.map((balance) => (
<Fragment key={balance.id}>
<div
Expand Down Expand Up @@ -39,7 +48,8 @@ export const Balances = () => {
<span
className={classNames([
"rounded-sm bg-[#1a1a1a] px-4 py-2 text-center font-bold",
balance.status === "initializing" && "text-brand-pink",
// TODO: Get initializing status from pool, it's no longer available on individual balances
// balance.status === "initializing" && "text-brand-pink",
balance.status === "cache" && "text-orange",
balance.status === "stale" && "text-brand-orange",
])}
Expand All @@ -66,6 +76,21 @@ export const Balances = () => {
) : null}
</span>

<span className="flex flex-col whitespace-nowrap">
<span className="whitespace-nowrap">
{formatDecimals(balance.total.tokens)} {balance.token?.symbol}
</span>
<span className="text-xs opacity-60">
{typeof balance.total.fiat("usd") === "number"
? new Intl.NumberFormat(undefined, {
style: "currency",
currency: "usd",
currencyDisplay: "narrowSymbol",
}).format(balance.total.fiat("usd") || 0)
: " -"}
</span>
</span>

<span className="flex flex-col whitespace-nowrap">
<span className="whitespace-nowrap">
{formatDecimals(balance.transferable.tokens)} {balance.token?.symbol}
Expand Down
4 changes: 3 additions & 1 deletion apps/balances-demo/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import "anylogger-loglevel"

import "./index.css"

import { BalancesProvider } from "@talismn/balances-react"
import loglevel from "loglevel"
import { StrictMode, useState } from "react"
import { createRoot } from "react-dom/client"

import { App } from "./App"

loglevel.setLevel("info")

const onfinalityApiKey = undefined

const Root = () => {
Expand Down
23 changes: 1 addition & 22 deletions packages/balances-react/src/atoms/balances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import {
Balance,
BalanceJson,
Balances,
HydrateDb,
balances as balancesFn,
deleteSubscriptionId,
getBalanceId,
getValidSubscriptionIds,
HydrateDb,
} from "@talismn/balances"
import { Token } from "@talismn/chaindata-provider"
import { isEthereumAddress } from "@talismn/util"
Expand Down Expand Up @@ -45,11 +43,6 @@ export const allBalancesAtom = atom(async (get) => {
get(balancesHydrateDataAtom),
])

const subscriptionIds = getValidSubscriptionIds()
if (subscriptionIds.size < 1) {
Object.values(balances).forEach((b) => (b.status = "cache"))
}

return new Balances(
Object.values(balances).filter((balance) => !!hydrateData?.tokens?.[balance.tokenId]),
// hydrate balance chains, evmNetworks, tokens and tokenRates
Expand Down Expand Up @@ -288,19 +281,6 @@ const balancesSubscriptionAtomEffect = atomEffect((get) => {
})
}, 30_000)

// TODO: Create subscriptions in a service worker, where we can detect page closes
// and therefore reliably delete the subscriptionId when the user closes our dapp
//
// For more information, check out https://developer.chrome.com/blog/page-lifecycle-api/#faqs
// and scroll down to:
// - `What is the back/forward cache?`, and
// - `If I can't run asynchronous APIs in the frozen or terminated states, how can I save data to IndexedDB?
//
// For now, we'll just last-ditch remove the subscriptionId (it works surprisingly well!) in the beforeunload event
window.onbeforeunload = () => {
deleteSubscriptionId()
}

return balanceModules.map((balanceModule) => {
const unsub = balancesFn(
balanceModule,
Expand Down Expand Up @@ -375,7 +355,6 @@ const balancesSubscriptionAtomEffect = atomEffect((get) => {
unsubs?.forEach((unsub) => unsub())
})
abort.signal.addEventListener("abort", () => setTimeout(unsubscribe, 2_000))
abort.signal.addEventListener("abort", () => deleteSubscriptionId())

return () => abort.abort("Unsubscribed")
})
5 changes: 1 addition & 4 deletions packages/balances-react/src/atoms/cryptoWaitReady.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { cryptoWaitReady } from "@polkadot/util-crypto"
import { watCryptoWaitReady } from "@talismn/scale"
import { atom } from "jotai"

export const cryptoWaitReadyAtom = atom(
async () => await Promise.all([cryptoWaitReady(), watCryptoWaitReady()])
)
export const cryptoWaitReadyAtom = atom(async () => await cryptoWaitReady())
36 changes: 23 additions & 13 deletions packages/balances-react/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
import { AnyBalanceModule, Hydrate } from "@talismn/balances"
import { useSetAtom } from "jotai"
import { ReactNode, useEffect } from "react"

import {
balanceModuleCreatorsAtom,
coingeckoConfigAtom,
enabledChainsAtom,
enabledTokensAtom,
enableTestnetsAtom,
onfinalityApiKeyAtom,
} from "./atoms/config"

export {
evmErc20TokenId,
evmNativeTokenId,
subNativeTokenId,
subEquilibriumTokenId,
subAssetTokenId,
subPsp22TokenId,
subTokensTokenId,
} from "@talismn/balances"

export * from "./hooks/useBalances"
export * from "./hooks/useChainConnectors"
export * from "./hooks/useChaindata"
Expand All @@ -13,19 +36,6 @@ export * from "./atoms/config"
export * from "./atoms/cryptoWaitReady"
export * from "./atoms/tokenRates"

import { AnyBalanceModule, Hydrate } from "@talismn/balances"
import { useSetAtom } from "jotai"
import { ReactNode, useEffect } from "react"

import {
balanceModuleCreatorsAtom,
coingeckoConfigAtom,
enableTestnetsAtom,
enabledChainsAtom,
enabledTokensAtom,
onfinalityApiKeyAtom,
} from "./atoms/config"

export type BalancesConfig = {
/**
* Optionally provide your own array of BalanceModules, when you don't want to use the defaults.
Expand Down
2 changes: 1 addition & 1 deletion packages/balances/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"@talismn/chain-connector-evm": "workspace:*",
"@talismn/chaindata-provider": "workspace:*",
"@talismn/scale": "workspace:*",
"@talismn/subshape-fork": "^0.0.2",
"@talismn/token-rates": "workspace:*",
"@talismn/util": "workspace:*",
"anylogger": "^1.0.11",
Expand All @@ -41,6 +40,7 @@
"lodash": "4.17.21",
"pako": "^2.1.0",
"rxjs": "^7.8.1",
"scale-ts": "^1.6.0",
"viem": "^2.8.18"
},
"devDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion packages/balances/src/BalanceModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ interface BalanceModuleSubstrate<
fetchSubstrateChainMeta(
chainId: ChainId,
moduleConfig?: TModuleConfig,
metadataRpc?: `0x${string}`
metadataRpc?: `0x${string}`,
systemProperties?: Record<string, any> // eslint-disable-line @typescript-eslint/no-explicit-any
): Promise<TChainMeta | null>

/** Detects which tokens are available on a given substrate chain */
Expand Down
57 changes: 48 additions & 9 deletions packages/balances/src/MiniMetadataUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {
fetchInitMiniMetadatas,
fetchMiniMetadatas,
} from "@talismn/chaindata-provider"
import { toHex } from "@talismn/scale"
import { liveQuery } from "dexie"
import isEqual from "lodash/isEqual"
import { from } from "rxjs"
import { Bytes, Option, u32 } from "scale-ts"

import { ChainConnectors } from "./BalanceModule"
import log from "./log"
Expand Down Expand Up @@ -244,7 +246,7 @@ export class MiniMetadataUpdater {
return []
})

const concurrency = 4
const concurrency = 12
;(
await PromisePool.withConcurrency(concurrency)
.for(needUpdates)
Expand All @@ -257,20 +259,57 @@ export class MiniMetadataUpdater {
if (specName === null) return
if (specVersion === null) return

const metadataRpc = await this.#chainConnectors.substrate?.send(
chainId,
"state_getMetadata",
[]
)
const fetchMetadata = async () => {
const errors: { v15: null | unknown; v14: null | unknown } = { v15: null, v14: null }

try {
const response = await this.#chainConnectors.substrate?.send(chainId, "state_call", [
"Metadata_metadata_at_version",
toHex(u32.enc(15)),
])
const result = response ? Option(Bytes()).dec(response) : null
if (result) return result
} catch (v15Cause) {
errors.v15 = v15Cause
}

try {
const response = await this.#chainConnectors.substrate?.send(
chainId,
"state_getMetadata",
[]
)
if (response) return response
} catch (v14Cause) {
errors.v14 = v14Cause
}

log.warn(
`Failed to fetch both metadata v15 and v14 for chain ${chainId}`,
errors.v15,
errors.v14
)
return null
}

const [metadataRpc, systemProperties] = await Promise.all([
fetchMetadata(),
this.#chainConnectors.substrate?.send(chainId, "system_properties", []),
])

for (const mod of this.#balanceModules.filter((m) => m.type.startsWith("substrate-"))) {
const balancesConfig = (chain.balancesConfig ?? []).find(
({ moduleType }) => moduleType === mod.type
)
const moduleConfig = balancesConfig?.moduleConfig ?? {}

const metadata = await mod.fetchSubstrateChainMeta(chainId, moduleConfig, metadataRpc)
const tokens = await mod.fetchSubstrateChainTokens(chainId, metadata, moduleConfig)
const chainMeta = await mod.fetchSubstrateChainMeta(
chainId,
moduleConfig,
metadataRpc,
systemProperties
)
const tokens = await mod.fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig)

// update tokens in chaindata
await this.#chaindataProvider.updateChainTokens(
Expand All @@ -281,7 +320,7 @@ export class MiniMetadataUpdater {
)

// update miniMetadatas
const { miniMetadata: data, metadataVersion: version, ...extra } = metadata ?? {}
const { miniMetadata: data, metadataVersion: version, ...extra } = chainMeta ?? {}
await balancesDb.miniMetadatas.put({
id: deriveMiniMetadataId({
source: mod.type,
Expand Down
Loading

0 comments on commit e02ec54

Please sign in to comment.