@talismn/balances-react 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/useBalances.d.ts +8 -0
- package/dist/hooks/useBalances.js +77 -0
- package/dist/hooks/useChaindata.d.ts +22 -0
- package/dist/hooks/useChaindata.js +120 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/log.d.ts +2 -0
- package/dist/log.js +5 -0
- package/package.json +68 -0
package/CHANGELOG.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# @talismn/balances-react
|
2
|
+
|
3
|
+
## 0.1.1
|
4
|
+
|
5
|
+
### Patch Changes
|
6
|
+
|
7
|
+
- Fixed publish config
|
8
|
+
- Updated dependencies
|
9
|
+
- @talismn/balances@0.1.1
|
10
|
+
- @talismn/balances-evm-erc20@0.1.1
|
11
|
+
- @talismn/balances-evm-native@0.1.1
|
12
|
+
- @talismn/balances-example@0.1.1
|
13
|
+
- @talismn/balances-substrate-native@0.1.1
|
14
|
+
- @talismn/balances-substrate-orml@0.1.1
|
15
|
+
- @talismn/chain-connector@0.1.1
|
16
|
+
- @talismn/chaindata-provider@0.1.1
|
17
|
+
- @talismn/chaindata-provider-extension@0.1.1
|
18
|
+
|
19
|
+
## 0.1.0
|
20
|
+
|
21
|
+
### Minor Changes
|
22
|
+
|
23
|
+
- 43c1a3a: Initial release
|
24
|
+
|
25
|
+
### Patch Changes
|
26
|
+
|
27
|
+
- Updated dependencies [43c1a3a]
|
28
|
+
- @talismn/balances@0.1.0
|
29
|
+
- @talismn/balances-evm-erc20@0.1.0
|
30
|
+
- @talismn/balances-evm-native@0.1.0
|
31
|
+
- @talismn/balances-example@0.1.0
|
32
|
+
- @talismn/balances-substrate-native@0.1.0
|
33
|
+
- @talismn/balances-substrate-orml@0.1.0
|
34
|
+
- @talismn/chain-connector@0.1.0
|
35
|
+
- @talismn/chaindata-provider@0.1.0
|
36
|
+
- @talismn/chaindata-provider-extension@0.1.0
|
package/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# @talismn/balances-react
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { AddressesByToken, BalanceJson, BalanceModule, Balances } from "@talismn/balances";
|
2
|
+
import { ChaindataProvider, Token } from "@talismn/chaindata-provider";
|
3
|
+
import { Dexie } from "dexie";
|
4
|
+
export declare function useBalances(balanceModules: Array<BalanceModule<any, any, any, any>>, chaindataProvider: ChaindataProvider | null, addressesByToken: AddressesByToken<Token> | null): Balances | undefined;
|
5
|
+
export declare class BalancesDatabase extends Dexie {
|
6
|
+
balances: Dexie.Table<BalanceJson, string>;
|
7
|
+
constructor();
|
8
|
+
}
|
@@ -0,0 +1,77 @@
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
8
|
+
});
|
9
|
+
};
|
10
|
+
import { Balances, balances as balancesFn, } from "@talismn/balances";
|
11
|
+
import { ChainConnector } from "@talismn/chain-connector";
|
12
|
+
import { Dexie } from "dexie";
|
13
|
+
import { useLiveQuery } from "dexie-react-hooks";
|
14
|
+
import { useEffect, useState } from "react";
|
15
|
+
import { useDebounce } from "react-use";
|
16
|
+
import log from "../log";
|
17
|
+
import { useChains, useEvmNetworks, useTokens } from "./useChaindata";
|
18
|
+
export function useBalances(
|
19
|
+
// TODO: Make this array of BalanceModules more type-safe
|
20
|
+
balanceModules, chaindataProvider, addressesByToken) {
|
21
|
+
const chainConnector = useChainConnector(chaindataProvider);
|
22
|
+
useEffect(() => {
|
23
|
+
if (chainConnector === null)
|
24
|
+
return;
|
25
|
+
if (chaindataProvider === null)
|
26
|
+
return;
|
27
|
+
if (addressesByToken === null)
|
28
|
+
return;
|
29
|
+
const unsubscribePromises = balanceModules.map((balanceModule) => balancesFn(balanceModule, chainConnector, chaindataProvider, addressesByToken, (error, balances) => {
|
30
|
+
if (error)
|
31
|
+
return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
|
32
|
+
if (!balances)
|
33
|
+
return;
|
34
|
+
db.transaction("rw", db.balances, () => __awaiter(this, void 0, void 0, function* () {
|
35
|
+
yield db.balances.bulkPut(Object.entries(balances.toJSON()).map(([id, balance]) => (Object.assign({ id }, balance))));
|
36
|
+
}));
|
37
|
+
}));
|
38
|
+
// TODO: Set balances status to cache on unmount
|
39
|
+
return () => {
|
40
|
+
unsubscribePromises.forEach((unsubscribePromise) => unsubscribePromise.then((unsub) => unsub()));
|
41
|
+
};
|
42
|
+
}, [addressesByToken, chainConnector]);
|
43
|
+
const chains = useChains(chaindataProvider);
|
44
|
+
const evmNetworks = useEvmNetworks(chaindataProvider);
|
45
|
+
const tokens = useTokens(chaindataProvider);
|
46
|
+
const balances = useLiveQuery(() => __awaiter(this, void 0, void 0, function* () { return new Balances(yield db.balances.toArray(), { chains, evmNetworks, tokens }); }), [chains, evmNetworks, tokens]);
|
47
|
+
// debounce every 100ms to prevent hammering UI with updates
|
48
|
+
const [debouncedBalances, setDebouncedBalances] = useState(balances);
|
49
|
+
useDebounce(() => balances && setDebouncedBalances(balances), 100, [balances]);
|
50
|
+
return debouncedBalances;
|
51
|
+
}
|
52
|
+
// TODO: Allow advanced users of this library to provide their own chain connector
|
53
|
+
function useChainConnector(chaindataProvider) {
|
54
|
+
const [chainConnector, setChainConnector] = useState(null);
|
55
|
+
useEffect(() => {
|
56
|
+
if (chaindataProvider === null)
|
57
|
+
return;
|
58
|
+
setChainConnector(new ChainConnector(chaindataProvider));
|
59
|
+
}, [chaindataProvider]);
|
60
|
+
return chainConnector;
|
61
|
+
}
|
62
|
+
export class BalancesDatabase extends Dexie {
|
63
|
+
constructor() {
|
64
|
+
super("Balances");
|
65
|
+
// https://dexie.org/docs/Tutorial/Design#database-versioning
|
66
|
+
this.version(1).stores({
|
67
|
+
// You only need to specify properties that you wish to index.
|
68
|
+
// The object store will allow any properties on your stored objects but you can only query them by indexed properties
|
69
|
+
// https://dexie.org/docs/API-Reference#declare-database
|
70
|
+
//
|
71
|
+
// Never index properties containing images, movies or large (huge) strings. Store them in IndexedDB, yes! but just don’t index them!
|
72
|
+
// https://dexie.org/docs/Version/Version.stores()#warning
|
73
|
+
balances: "id, source, status, address, tokenId",
|
74
|
+
});
|
75
|
+
}
|
76
|
+
}
|
77
|
+
const db = new BalancesDatabase();
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { Chain, ChainId, ChainList, ChaindataProvider, EvmNetwork, EvmNetworkId, EvmNetworkList, Token, TokenId, TokenList } from "@talismn/chaindata-provider";
|
2
|
+
export declare function useChaindata(): (ChaindataProvider & {
|
3
|
+
generation?: number | undefined;
|
4
|
+
}) | null;
|
5
|
+
export declare function useChains(chaindata: (ChaindataProvider & {
|
6
|
+
generation?: number;
|
7
|
+
}) | null): ChainList;
|
8
|
+
export declare function useChain(chaindata: (ChaindataProvider & {
|
9
|
+
generation?: number;
|
10
|
+
}) | null, chainId?: ChainId): Chain | null | undefined;
|
11
|
+
export declare function useEvmNetworks(chaindata: (ChaindataProvider & {
|
12
|
+
generation?: number;
|
13
|
+
}) | null): EvmNetworkList;
|
14
|
+
export declare function useEvmNetwork(chaindata: (ChaindataProvider & {
|
15
|
+
generation?: number;
|
16
|
+
}) | null, evmNetworkId?: EvmNetworkId): EvmNetwork | null | undefined;
|
17
|
+
export declare function useTokens(chaindata: (ChaindataProvider & {
|
18
|
+
generation?: number;
|
19
|
+
}) | null): TokenList;
|
20
|
+
export declare function useToken(chaindata: (ChaindataProvider & {
|
21
|
+
generation?: number;
|
22
|
+
}) | null, tokenId?: TokenId): Token | null | undefined;
|
@@ -0,0 +1,120 @@
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
8
|
+
});
|
9
|
+
};
|
10
|
+
import { ChaindataProviderExtension } from "@talismn/chaindata-provider-extension";
|
11
|
+
import { useEffect, useState } from "react";
|
12
|
+
import log from "../log";
|
13
|
+
export function useChaindata() {
|
14
|
+
const [chaindataProvider, setChaindataProvider] = useState(null);
|
15
|
+
// this number is incremented each time the chaindataProvider has fetched new data
|
16
|
+
const [generation, setGeneration] = useState(0);
|
17
|
+
useEffect(() => {
|
18
|
+
const chaindataProvider = new ChaindataProviderExtension();
|
19
|
+
let shouldHydrate = true;
|
20
|
+
const timer = 300000; // 300_000ms = 300s = 5 minutes
|
21
|
+
const hydrate = () => __awaiter(this, void 0, void 0, function* () {
|
22
|
+
if (!shouldHydrate)
|
23
|
+
return;
|
24
|
+
try {
|
25
|
+
const updated = yield chaindataProvider.hydrate();
|
26
|
+
if (updated)
|
27
|
+
setGeneration((generation) => (generation + 1) % Number.MAX_SAFE_INTEGER);
|
28
|
+
setTimeout(hydrate, timer);
|
29
|
+
}
|
30
|
+
catch (error) {
|
31
|
+
const retryTimeout = 5000; // 5_000ms = 5 seconds
|
32
|
+
log.error(`Failed to fetch chaindata, retrying in ${Math.round(retryTimeout / 1000)} seconds`, error);
|
33
|
+
setTimeout(hydrate, retryTimeout);
|
34
|
+
}
|
35
|
+
});
|
36
|
+
setChaindataProvider(chaindataProvider);
|
37
|
+
hydrate();
|
38
|
+
return () => {
|
39
|
+
shouldHydrate = false;
|
40
|
+
};
|
41
|
+
}, []);
|
42
|
+
if (chaindataProvider)
|
43
|
+
chaindataProvider.generation = generation;
|
44
|
+
return chaindataProvider;
|
45
|
+
}
|
46
|
+
export function useChains(chaindata) {
|
47
|
+
const [chains, setChains] = useState();
|
48
|
+
useEffect(() => {
|
49
|
+
if (!chaindata)
|
50
|
+
return;
|
51
|
+
const thisGeneration = chaindata.generation;
|
52
|
+
chaindata.chains().then((chains) => {
|
53
|
+
if (thisGeneration !== chaindata.generation)
|
54
|
+
return;
|
55
|
+
setChains(chains);
|
56
|
+
});
|
57
|
+
}, [chaindata === null || chaindata === void 0 ? void 0 : chaindata.generation]);
|
58
|
+
return chains || {};
|
59
|
+
}
|
60
|
+
export function useChain(chaindata, chainId) {
|
61
|
+
const [chain, setChain] = useState();
|
62
|
+
useEffect(() => {
|
63
|
+
if (chaindata === null)
|
64
|
+
return;
|
65
|
+
if (!chainId)
|
66
|
+
return;
|
67
|
+
chaindata.getChain(chainId).then(setChain);
|
68
|
+
}, [chaindata === null || chaindata === void 0 ? void 0 : chaindata.generation]);
|
69
|
+
return chain;
|
70
|
+
}
|
71
|
+
export function useEvmNetworks(chaindata) {
|
72
|
+
const [evmNetworks, setEvmNetworks] = useState();
|
73
|
+
useEffect(() => {
|
74
|
+
if (!chaindata)
|
75
|
+
return;
|
76
|
+
const thisGeneration = chaindata.generation;
|
77
|
+
chaindata.evmNetworks().then((evmNetworks) => {
|
78
|
+
if (thisGeneration !== chaindata.generation)
|
79
|
+
return;
|
80
|
+
setEvmNetworks(evmNetworks);
|
81
|
+
});
|
82
|
+
}, [chaindata === null || chaindata === void 0 ? void 0 : chaindata.generation]);
|
83
|
+
return evmNetworks || {};
|
84
|
+
}
|
85
|
+
export function useEvmNetwork(chaindata, evmNetworkId) {
|
86
|
+
const [evmNetwork, setEvmNetwork] = useState();
|
87
|
+
useEffect(() => {
|
88
|
+
if (chaindata === null)
|
89
|
+
return;
|
90
|
+
if (!evmNetworkId)
|
91
|
+
return;
|
92
|
+
chaindata.getEvmNetwork(evmNetworkId).then(setEvmNetwork);
|
93
|
+
}, [chaindata === null || chaindata === void 0 ? void 0 : chaindata.generation]);
|
94
|
+
return evmNetwork;
|
95
|
+
}
|
96
|
+
export function useTokens(chaindata) {
|
97
|
+
const [tokens, setTokens] = useState();
|
98
|
+
useEffect(() => {
|
99
|
+
if (!chaindata)
|
100
|
+
return;
|
101
|
+
const thisGeneration = chaindata.generation;
|
102
|
+
chaindata.tokens().then((tokens) => {
|
103
|
+
if (thisGeneration !== chaindata.generation)
|
104
|
+
return;
|
105
|
+
setTokens(tokens);
|
106
|
+
});
|
107
|
+
}, [chaindata === null || chaindata === void 0 ? void 0 : chaindata.generation]);
|
108
|
+
return tokens || {};
|
109
|
+
}
|
110
|
+
export function useToken(chaindata, tokenId) {
|
111
|
+
const [token, setToken] = useState();
|
112
|
+
useEffect(() => {
|
113
|
+
if (chaindata === null)
|
114
|
+
return;
|
115
|
+
if (!tokenId)
|
116
|
+
return;
|
117
|
+
chaindata.getToken(tokenId).then(setToken);
|
118
|
+
}, [chaindata === null || chaindata === void 0 ? void 0 : chaindata.generation]);
|
119
|
+
return token;
|
120
|
+
}
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./hooks";
|
package/dist/index.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./hooks";
|
package/dist/log.d.ts
ADDED
package/dist/log.js
ADDED
package/package.json
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
{
|
2
|
+
"name": "@talismn/balances-react",
|
3
|
+
"version": "0.1.1",
|
4
|
+
"author": "Talisman",
|
5
|
+
"homepage": "https://talisman.xyz",
|
6
|
+
"license": "UNLICENSED",
|
7
|
+
"publishConfig": {
|
8
|
+
"access": "public"
|
9
|
+
},
|
10
|
+
"repository": {
|
11
|
+
"directory": "packages/balances-react",
|
12
|
+
"type": "git",
|
13
|
+
"url": "https://github.com/talismansociety/talisman.git"
|
14
|
+
},
|
15
|
+
"main": "dist/index.js",
|
16
|
+
"types": "dist/index.d.ts",
|
17
|
+
"files": [
|
18
|
+
"/dist"
|
19
|
+
],
|
20
|
+
"engines": {
|
21
|
+
"node": ">=14"
|
22
|
+
},
|
23
|
+
"scripts": {
|
24
|
+
"dev": "tsc --watch --declarationMap",
|
25
|
+
"build": "tsc --declarationMap",
|
26
|
+
"build:packages:prod": "rm -rf dist && tsc",
|
27
|
+
"prepack": "yarn build:packages:prod",
|
28
|
+
"test": "jest",
|
29
|
+
"lint": "eslint . --max-warnings 0",
|
30
|
+
"clean": "rm -rf dist && rm -rf .turbo rm -rf node_modules"
|
31
|
+
},
|
32
|
+
"dependencies": {
|
33
|
+
"@talismn/balances": "^0.1.1",
|
34
|
+
"@talismn/balances-evm-erc20": "^0.1.1",
|
35
|
+
"@talismn/balances-evm-native": "^0.1.1",
|
36
|
+
"@talismn/balances-example": "^0.1.1",
|
37
|
+
"@talismn/balances-substrate-native": "^0.1.1",
|
38
|
+
"@talismn/balances-substrate-orml": "^0.1.1",
|
39
|
+
"@talismn/chain-connector": "^0.1.1",
|
40
|
+
"@talismn/chaindata-provider": "^0.1.1",
|
41
|
+
"@talismn/chaindata-provider-extension": "^0.1.1",
|
42
|
+
"anylogger": "^1.0.11",
|
43
|
+
"dexie": "^3.2.2",
|
44
|
+
"dexie-react-hooks": "^1.1.1",
|
45
|
+
"react-use": "^17.4.0"
|
46
|
+
},
|
47
|
+
"devDependencies": {
|
48
|
+
"@talismn/eslint-config": "^0.0.0",
|
49
|
+
"@talismn/tsconfig": "^0.0.0",
|
50
|
+
"@types/jest": "^27.5.1",
|
51
|
+
"@types/react": "^18.0.17",
|
52
|
+
"eslint": "^8.4.0",
|
53
|
+
"jest": "^28.1.0",
|
54
|
+
"react": "^18.2.0",
|
55
|
+
"ts-jest": "^28.0.2",
|
56
|
+
"typescript": "^4.6.4"
|
57
|
+
},
|
58
|
+
"peerDependencies": {
|
59
|
+
"react": "*",
|
60
|
+
"react-dom": "*"
|
61
|
+
},
|
62
|
+
"eslintConfig": {
|
63
|
+
"root": true,
|
64
|
+
"extends": [
|
65
|
+
"@talismn/eslint-config"
|
66
|
+
]
|
67
|
+
}
|
68
|
+
}
|