diff --git a/package.json b/package.json index dd83a5d6d2..13f84d5d09 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,13 @@ "packages/*" ], "resolutions": { - "@polkadot/api": "^4.17.2-12", - "@polkadot/api-contract": "^4.17.2-12", + "@polkadot/api": "^4.17.2-14", + "@polkadot/api-contract": "^4.17.2-14", "@polkadot/hw-ledger": "^6.11.2-10", "@polkadot/keyring": "^6.11.2-10", "@polkadot/networks": "^6.11.2-10", "@polkadot/phishing": "^0.6.220", - "@polkadot/types": "^4.17.2-12", + "@polkadot/types": "^4.17.2-14", "@polkadot/util": "^6.11.2-10", "@polkadot/util-crypto": "^6.11.2-10", "@polkadot/wasm-crypto": "^4.1.1", @@ -74,7 +74,7 @@ "@crustio/crust-pin": "^0.0.1", "@pinata/sdk": "^1.1.23", "@polkadot/dev": "^0.62.52", - "@polkadot/ts": "^0.4.3", + "@polkadot/ts": "^0.4.4", "@types/bn.js": "^4.11.6", "@types/chart.js": "^2.9.32", "@types/file-saver": "^2.0.2", diff --git a/packages/page-contracts/package.json b/packages/page-contracts/package.json index d6ff91f218..846e29b040 100644 --- a/packages/page-contracts/package.json +++ b/packages/page-contracts/package.json @@ -6,6 +6,6 @@ "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.14.6", - "@polkadot/api-contract": "^4.17.2-12" + "@polkadot/api-contract": "^4.17.2-14" } } diff --git a/packages/page-parachains/src/Crowdloan/Fund.tsx b/packages/page-parachains/src/Crowdloan/Fund.tsx index c4aabb10b2..7ea9a4568a 100644 --- a/packages/page-parachains/src/Crowdloan/Fund.tsx +++ b/packages/page-parachains/src/Crowdloan/Fund.tsx @@ -31,12 +31,12 @@ interface LastChange { prevLength: number; } -function Fund ({ bestHash, bestNumber, className, isOdd, isOngoing, leasePeriod, value: { childKey, info: { cap, depositor, end, firstPeriod, lastPeriod, raised, verifier }, isCapped, isEnded, isWinner, paraId } }: Props): React.ReactElement { +function Fund ({ bestHash, bestNumber, className, isOdd, isOngoing, leasePeriod, value: { info: { cap, depositor, end, firstPeriod, lastPeriod, raised, verifier }, isCapped, isEnded, isWinner, paraId } }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const { isAccount } = useAccounts(); const endpoints = useParaEndpoints(paraId); - const { blockHash: contribHash, contributorsHex, myAccounts, myAccountsHex, myContributions } = useContributions(paraId, childKey); + const { blockHash, contributorsHex, hasLoaded, myAccounts, myAccountsHex, myContributions } = useContributions(paraId); const [lastChange, setLastChange] = useState(() => ({ prevHash: '', prevLength: 0 })); const isDepositor = useMemo( @@ -73,10 +73,10 @@ function Fund ({ bestHash, bestNumber, className, isOdd, isOngoing, leasePeriod, const prevLength = contributorsHex.length; return prev.prevLength !== prevLength - ? { prevHash: contribHash, prevLength } + ? { prevHash: blockHash, prevLength } : prev; }); - }, [contributorsHex, contribHash]); + }, [contributorsHex, blockHash]); return ( @@ -132,7 +132,7 @@ function Fund ({ bestHash, bestNumber, className, isOdd, isOngoing, leasePeriod, )} - {contribHash === '-' + {!hasLoaded ? : ( <> diff --git a/packages/page-parachains/src/Crowdloan/useContributions.ts b/packages/page-parachains/src/Crowdloan/useContributions.ts index 101befd88a..1cd8a63619 100644 --- a/packages/page-parachains/src/Crowdloan/useContributions.ts +++ b/packages/page-parachains/src/Crowdloan/useContributions.ts @@ -1,195 +1,55 @@ // Copyright 2017-2021 @polkadot/app-parachains authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { ApiPromise } from '@polkadot/api'; -import type { StorageKey } from '@polkadot/types'; -import type { Balance, EventRecord, ParaId } from '@polkadot/types/interfaces'; +import type { DeriveContributions, DeriveOwnContributions } from '@polkadot/api-derive/types'; +import type { Balance, ParaId } from '@polkadot/types/interfaces'; -import { useCallback, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; -import { useAccounts, useApi, useEventTrigger, useIsMountedRef } from '@polkadot/react-hooks'; +import { useAccounts, useApi, useCall } from '@polkadot/react-hooks'; import { encodeAddress } from '@polkadot/util-crypto'; -interface Contributions { - contributorsHex: string[]; - contributorsMap: Record; - hasKeys: boolean; +interface Result extends DeriveContributions { + hasLoaded: boolean; myAccounts: string[]; myAccountsHex: string[]; myContributions: Record; } -interface Result extends Contributions { - blockHash: string; -} - const NO_CONTRIB: Result = { blockHash: '-', contributorsHex: [], - contributorsMap: {}, - hasKeys: false, + hasLoaded: false, myAccounts: [], myAccountsHex: [], myContributions: {} }; -function extractContributors (keys: StorageKey[], { myContributions }: Contributions, allAccountsHex: string[], ss58Format?: number): Contributions { - const contributorsMap: Record = {}; - - keys.forEach((k): void => { - contributorsMap[k.toHex()] = true; - }); - - const myAccountsHex = allAccountsHex.filter((h) => contributorsMap[h]); - - return { - contributorsHex: Object.keys(contributorsMap), - contributorsMap, - hasKeys: true, - myAccounts: myAccountsHex.map((a) => encodeAddress(a, ss58Format)), - myAccountsHex, - myContributions - }; -} - -function extractDelta (api: ApiPromise, records: EventRecord[], { contributorsMap, hasKeys, myContributions }: Contributions, allAccountsHex: string[], ss58Format?: number): Contributions { - records.forEach((record): void => { - const hex = record.event.data[0].toHex(); - - if (api.events.crowdloan.Withdrew.is(record.event)) { - delete contributorsMap[hex]; - } else { - contributorsMap[hex] = true; - } - }); - - const myAccountsHex = allAccountsHex.filter((h) => contributorsMap[h]); - - return { - contributorsHex: Object.keys(contributorsMap), - contributorsMap, - hasKeys, - myAccounts: myAccountsHex.map((a) => encodeAddress(a, ss58Format)), - myAccountsHex, - myContributions - }; -} - -function useMyContributions (paraId: ParaId, childKey: string, keys: string[]): Record { +export default function useContributions (paraId: ParaId): Result { const { api } = useApi(); const { allAccountsHex } = useAccounts(); - const mountedRef = useIsMountedRef(); - const [state, setState] = useState>({}); - - const trigger = useEventTrigger([ - api.events.crowdloan.Contributed, - api.events.crowdloan.Withdrew - ], useCallback( - ({ event: { data: [accountId, eventParaId] } }: EventRecord) => - eventParaId.eq(paraId) && - allAccountsHex.some((a) => accountId.eq(a)), - [allAccountsHex, paraId] - )); + const [state, setState] = useState(() => NO_CONTRIB); + const derive = useCall(api.derive.crowdloan.contributions, [paraId]); + const myContributions = useCall(api.derive.crowdloan.ownContributions, [paraId, state.myAccountsHex]); useEffect((): void => { - if (trigger && keys.length) { - Promise - .all(keys.map((k) => api.rpc.childstate.getStorage(childKey, k))) - .then((values) => - mountedRef.current && setState( - values - .map((v) => api.createType('Option', v)) - .map((o) => - o.isSome - ? api.createType('Balance', o.unwrap()) - : api.createType('Balance') - ) - .reduce((all, b, index) => ({ ...all, [keys[index]]: b }), {}) - ) - ) - .catch(console.error); - } else { - setState({}); - } - }, [api, childKey, keys, mountedRef, trigger]); + derive && setState((prev): Result => { + let myAccountsHex = derive.contributorsHex.filter((h) => allAccountsHex.includes(h)); + let myAccounts: string[]; - return state; -} + if (myAccountsHex.length === prev.myAccountsHex.length) { + myAccountsHex = prev.myAccountsHex; + myAccounts = prev.myAccounts; + } else { + myAccounts = myAccountsHex.map((a) => encodeAddress(a, api.registry.chainSS58)); + } -export default function useContributions (paraId: ParaId, childKey: string): Result { - const { api } = useApi(); - const { allAccountsHex } = useAccounts(); - const mountedRef = useIsMountedRef(); - const [state, _setState] = useState(() => NO_CONTRIB); - const myContributions = useMyContributions(paraId, childKey, state.myAccountsHex); + return { ...prev, ...derive, hasLoaded: true, myAccounts, myAccountsHex }; + }); + }, [api, allAccountsHex, derive]); - // these trigger a full refresh of the keys [ParaId] - const triggerAll = useEventTrigger([ - api.events.crowdloan.Dissolved, - api.events.crowdloan.AllRefunded, - api.events.crowdloan.PartiallyRefunded - ], useCallback( - ({ event: { data: [eventParaId] } }: EventRecord) => - eventParaId.eq(paraId), - [paraId] - )); - - // these trigger a delta adjustment [AccountId, ParaId, Balance] - const triggerDelta = useEventTrigger([ - api.events.crowdloan.Contributed, - api.events.crowdloan.Withdrew - ], useCallback( - ({ event: { data: [, eventParaId] } }: EventRecord) => - eventParaId.eq(paraId), - [paraId] - )); - - const setState = useCallback( - (blockHash: string, fn: (prev: Contributions) => Contributions) => - _setState((prev) => { - const { contributorsHex, contributorsMap, hasKeys, myAccounts, myAccountsHex } = fn(prev); - - return hasKeys - ? { - ...prev, - blockHash, - contributorsHex, - contributorsMap, - hasKeys, - ...(prev.myAccounts.length === myAccounts.length - ? {} - : { myAccounts, myAccountsHex } - ) - } - : prev; - }), - [] - ); - - // first just adjust deltas useEffect((): void => { - mountedRef.current && triggerDelta && triggerDelta.events.length && - setState(triggerDelta.blockHash, (prev) => - extractDelta(api, triggerDelta.events, prev, allAccountsHex, api.registry.chainSS58) - ); - }, [api, allAccountsHex, mountedRef, setState, triggerDelta]); - - // refreshing the whole map, all keys - useEffect((): void => { - triggerAll && - api.rpc.childstate - .getKeys(childKey, '0x') - .then((keys) => - mountedRef.current && setState(triggerAll.blockHash, (prev) => - extractContributors(keys, prev, allAccountsHex, api.registry.chainSS58) - ) - ) - .catch(console.error); - }, [allAccountsHex, api, childKey, mountedRef, setState, triggerAll]); - - // update when the contribution amounts have changed - useEffect((): void => { - _setState((prev) => ({ + myContributions && setState((prev) => ({ ...prev, myContributions })); diff --git a/packages/page-parachains/src/types.ts b/packages/page-parachains/src/types.ts index d3d899a7f5..0430657782 100644 --- a/packages/page-parachains/src/types.ts +++ b/packages/page-parachains/src/types.ts @@ -49,7 +49,6 @@ export interface Campaigns { } export interface Campaign extends WinnerData { - childKey: string; info: FundInfo; isCapped?: boolean; isEnded?: boolean; diff --git a/packages/page-parachains/src/useFunds.ts b/packages/page-parachains/src/useFunds.ts index 3501008ab6..5f47e3e080 100644 --- a/packages/page-parachains/src/useFunds.ts +++ b/packages/page-parachains/src/useFunds.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { Option, StorageKey } from '@polkadot/types'; -import type { AccountId, BalanceOf, BlockNumber, FundInfo, ParaId, TrieIndex } from '@polkadot/types/interfaces'; +import type { AccountId, BalanceOf, BlockNumber, FundInfo, ParaId } from '@polkadot/types/interfaces'; import type { ITuple } from '@polkadot/types/types'; import type { Campaign, Campaigns } from './types'; @@ -10,8 +10,8 @@ import BN from 'bn.js'; import { useEffect, useState } from 'react'; import { useApi, useBestNumber, useCall, useEventTrigger, useIsMountedRef, useMapKeys } from '@polkadot/react-hooks'; -import { BN_ZERO, u8aConcat, u8aToHex } from '@polkadot/util'; -import { blake2AsU8a, encodeAddress } from '@polkadot/util-crypto'; +import { BN_ZERO, u8aConcat } from '@polkadot/util'; +import { encodeAddress } from '@polkadot/util-crypto'; import { CROWD_PREFIX } from './constants'; @@ -37,17 +37,6 @@ function hasLease (paraId: ParaId, leased: ParaId[]): boolean { return leased.some((l) => l.eq(paraId)); } -function createChildKey (trieIndex: TrieIndex): string { - return u8aToHex( - u8aConcat( - ':child_storage:default:', - blake2AsU8a( - u8aConcat('crowdloan', trieIndex.toU8a()) - ) - ) - ); -} - // map into a campaign function updateFund (bestNumber: BN, minContribution: BN, data: Campaign, leased: ParaId[]): Campaign { data.isCapped = data.info.cap.sub(data.info.raised).lt(minContribution); @@ -129,7 +118,6 @@ const optFundMulti = { .filter((v): v is [ParaId, FundInfo] => !!v[1]) .map(([paraId, info]): Campaign => ({ accountId: encodeAddress(createAddress(paraId)), - childKey: createChildKey(info.trieIndex), firstSlot: info.firstPeriod, info, isCrowdloan: true, diff --git a/packages/react-api/package.json b/packages/react-api/package.json index 0f8116b78d..8fd7bbfa92 100644 --- a/packages/react-api/package.json +++ b/packages/react-api/package.json @@ -6,7 +6,7 @@ "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.14.6", - "@polkadot/api": "^4.17.2-12", + "@polkadot/api": "^4.17.2-14", "@polkadot/extension-dapp": "^0.38.8", "@substrate/connect": "^0.3.10", "fflate": "^0.7.1", diff --git a/yarn.lock b/yarn.lock index 754e108e92..367245a93e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2264,50 +2264,50 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-contract@npm:^4.17.2-12": - version: 4.17.2-12 - resolution: "@polkadot/api-contract@npm:4.17.2-12" +"@polkadot/api-contract@npm:^4.17.2-14": + version: 4.17.2-14 + resolution: "@polkadot/api-contract@npm:4.17.2-14" dependencies: "@babel/runtime": ^7.14.6 - "@polkadot/api": 4.17.2-12 - "@polkadot/types": 4.17.2-12 + "@polkadot/api": 4.17.2-14 + "@polkadot/types": 4.17.2-14 "@polkadot/util": ^6.11.2-10 rxjs: ^7.2.0 - checksum: f068a3bb42be626df618964e00564744ac881c6299d60b0cff8885ec4cbaaeeb9e5dbbda1e7c13bdd2dafe81ca4715ac922a32ea0d0ff2441c432091fdced37f + checksum: 145c3c8b32acfb2f25d0936340ad3c77368ea66e0c7473679156bdaae2296a436068185dc321ae53741ac063c09e7c1035a9eb897ca4498e3e413d5e1b440159 languageName: node linkType: hard -"@polkadot/api-derive@npm:4.17.2-12": - version: 4.17.2-12 - resolution: "@polkadot/api-derive@npm:4.17.2-12" +"@polkadot/api-derive@npm:4.17.2-14": + version: 4.17.2-14 + resolution: "@polkadot/api-derive@npm:4.17.2-14" dependencies: "@babel/runtime": ^7.14.6 - "@polkadot/api": 4.17.2-12 - "@polkadot/rpc-core": 4.17.2-12 - "@polkadot/types": 4.17.2-12 + "@polkadot/api": 4.17.2-14 + "@polkadot/rpc-core": 4.17.2-14 + "@polkadot/types": 4.17.2-14 "@polkadot/util": ^6.11.2-10 "@polkadot/util-crypto": ^6.11.2-10 rxjs: ^7.2.0 - checksum: c4c6251cc33f913cb21b0f24c7a4166b455e4e2b717cd8d905d2dfb79327aa059be733bb72f798227badd9efc5427a23a0b99ce1d3de5ca188ce5b6d3a49f8ee + checksum: 46b4eb46433ff627a392647a7d25788810c2f0572c1df9e5ab698cfd08c69d9fe89502d4266bb66d4ebcc05ba53356cc596912ce2692edafe7c3c722afe3ce3b languageName: node linkType: hard -"@polkadot/api@npm:^4.17.2-12": - version: 4.17.2-12 - resolution: "@polkadot/api@npm:4.17.2-12" +"@polkadot/api@npm:^4.17.2-14": + version: 4.17.2-14 + resolution: "@polkadot/api@npm:4.17.2-14" dependencies: "@babel/runtime": ^7.14.6 - "@polkadot/api-derive": 4.17.2-12 + "@polkadot/api-derive": 4.17.2-14 "@polkadot/keyring": ^6.11.2-10 - "@polkadot/rpc-core": 4.17.2-12 - "@polkadot/rpc-provider": 4.17.2-12 - "@polkadot/types": 4.17.2-12 - "@polkadot/types-known": 4.17.2-12 + "@polkadot/rpc-core": 4.17.2-14 + "@polkadot/rpc-provider": 4.17.2-14 + "@polkadot/types": 4.17.2-14 + "@polkadot/types-known": 4.17.2-14 "@polkadot/util": ^6.11.2-10 "@polkadot/util-crypto": ^6.11.2-10 eventemitter3: ^4.0.7 rxjs: ^7.2.0 - checksum: 3461a2fdce26635e2685c684f53afdfd57d3952b8e32900ea7a6a5419e5f0ff1f5f36359c4461f59b58ddc4fdc1c29180f5e5a2e84bf1f2a9728e49ff24a12c4 + checksum: e79c899b37f8fafebd9f723489d721474de9c40e7e24e59b24870a1cb2fa051bc88fee6118f36fb2873b5833fcd62a96b7b9e1cf11fe48b08199599aecfcac5f languageName: node linkType: hard @@ -2380,7 +2380,7 @@ __metadata: resolution: "@polkadot/app-contracts@workspace:packages/page-contracts" dependencies: "@babel/runtime": ^7.14.6 - "@polkadot/api-contract": ^4.17.2-12 + "@polkadot/api-contract": ^4.17.2-14 languageName: unknown linkType: soft @@ -2849,7 +2849,7 @@ __metadata: resolution: "@polkadot/react-api@workspace:packages/react-api" dependencies: "@babel/runtime": ^7.14.6 - "@polkadot/api": ^4.17.2-12 + "@polkadot/api": ^4.17.2-14 "@polkadot/extension-dapp": ^0.38.8 "@substrate/connect": ^0.3.10 fflate: ^0.7.1 @@ -2981,32 +2981,32 @@ __metadata: languageName: unknown linkType: soft -"@polkadot/rpc-core@npm:4.17.2-12": - version: 4.17.2-12 - resolution: "@polkadot/rpc-core@npm:4.17.2-12" +"@polkadot/rpc-core@npm:4.17.2-14": + version: 4.17.2-14 + resolution: "@polkadot/rpc-core@npm:4.17.2-14" dependencies: "@babel/runtime": ^7.14.6 - "@polkadot/rpc-provider": 4.17.2-12 - "@polkadot/types": 4.17.2-12 + "@polkadot/rpc-provider": 4.17.2-14 + "@polkadot/types": 4.17.2-14 "@polkadot/util": ^6.11.2-10 rxjs: ^7.2.0 - checksum: aa8996f45fa5011d5edbffd9e920d86e3e42f15e1230b6eaf86394095c0ccdb53fa617060fc24eab52b5614aa50d96c764d746e37021be25811b45ac6eee97c8 + checksum: 8a4647b956db1b3a5b69e85f8abb4e6436461bffb623f250ef368e0112c14b661e510d209d2748fa842dbc0b6c4529b82c6298135b15bd5f01510a1d340bee9b languageName: node linkType: hard -"@polkadot/rpc-provider@npm:4.17.2-12": - version: 4.17.2-12 - resolution: "@polkadot/rpc-provider@npm:4.17.2-12" +"@polkadot/rpc-provider@npm:4.17.2-14": + version: 4.17.2-14 + resolution: "@polkadot/rpc-provider@npm:4.17.2-14" dependencies: "@babel/runtime": ^7.14.6 - "@polkadot/types": 4.17.2-12 + "@polkadot/types": 4.17.2-14 "@polkadot/util": ^6.11.2-10 "@polkadot/util-crypto": ^6.11.2-10 "@polkadot/x-fetch": ^6.11.2-10 "@polkadot/x-global": ^6.11.2-10 "@polkadot/x-ws": ^6.11.2-10 eventemitter3: ^4.0.7 - checksum: fb2168d9d4fa1136861130870a189e595376a3148db363a18bf8e677c3b04877801a26609434965707527b14c2b1dbd3682937b0135fa78b0faa4260e80acc98 + checksum: 84cd7345fc061b13ee153d598e99247130f7033edce1bc6a7e4fff79c8961d5ecab2ffc988de7208a83287c51436d7340137828663870e956cc2374d2a65c217 languageName: node linkType: hard @@ -3037,36 +3037,36 @@ __metadata: languageName: unknown linkType: soft -"@polkadot/ts@npm:^0.4.3": - version: 0.4.3 - resolution: "@polkadot/ts@npm:0.4.3" +"@polkadot/ts@npm:^0.4.4": + version: 0.4.4 + resolution: "@polkadot/ts@npm:0.4.4" dependencies: "@types/chrome": ^0.0.145 - checksum: cf04c92473aebe13d89f7ddd7bff44ba334c7156fcc6e89ec0a1a91743149320010e3527e7ea7b224a215616d1c9e2abdc5c79c761b2654f0a4a105b1f38e80e + checksum: ef4cd0e3978b2e9e5b6ffb9d1e482c5f4ad800c7d570037df2472e9f34a07c2812b14e84ffd9272c09ae6afeb2c686b98a1baa302b5428afbd9c8140c46cca72 languageName: node linkType: hard -"@polkadot/types-known@npm:4.17.2-12": - version: 4.17.2-12 - resolution: "@polkadot/types-known@npm:4.17.2-12" +"@polkadot/types-known@npm:4.17.2-14": + version: 4.17.2-14 + resolution: "@polkadot/types-known@npm:4.17.2-14" dependencies: "@babel/runtime": ^7.14.6 "@polkadot/networks": ^6.11.2-10 - "@polkadot/types": 4.17.2-12 + "@polkadot/types": 4.17.2-14 "@polkadot/util": ^6.11.2-10 - checksum: 5baf9641358406cf8d3a7eee27c3685ab1baab8d528732e2d8090d002862f027b3f0f8854700adf5256f67cbb6c37019e88c3afa3525b90eb6b231d1ef4605fd + checksum: 6e0b8a2e09e2e53d0fee303b35e0284c6d9c15624aa1e68dfee4a7cbaa6307d626bce0b35b8c101a07aa552eacf85ab777bdfebceb8f783028b6fee3aaffbeb2 languageName: node linkType: hard -"@polkadot/types@npm:^4.17.2-12": - version: 4.17.2-12 - resolution: "@polkadot/types@npm:4.17.2-12" +"@polkadot/types@npm:^4.17.2-14": + version: 4.17.2-14 + resolution: "@polkadot/types@npm:4.17.2-14" dependencies: "@babel/runtime": ^7.14.6 "@polkadot/util": ^6.11.2-10 "@polkadot/util-crypto": ^6.11.2-10 rxjs: ^7.2.0 - checksum: ab49b94cb81d1da66a37b988caacacaf46f0384780cb96c4a73d5ce86a8bdc5c86cfc43f5208c7c3317cfd0d1f4ac176a97459c9665fb22adf5ffc19e24ea7e5 + checksum: 3ae418ae98d5430dc08e40be31fb1305fd80fb228fa1c13e865602797c0f31f7d2bccb9beb7c91da1426bbd015b92df533371bc77cc1fe5656da069401518e72 languageName: node linkType: hard @@ -15318,7 +15318,7 @@ __metadata: "@crustio/crust-pin": ^0.0.1 "@pinata/sdk": ^1.1.23 "@polkadot/dev": ^0.62.52 - "@polkadot/ts": ^0.4.3 + "@polkadot/ts": ^0.4.4 "@types/bn.js": ^4.11.6 "@types/chart.js": ^2.9.32 "@types/file-saver": ^2.0.2