@deserialize/multi-vm-wallet 1.6.0 → 1.6.2
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.
|
@@ -17,6 +17,9 @@ export interface PocketBalance {
|
|
|
17
17
|
balances: {
|
|
18
18
|
token: string;
|
|
19
19
|
balance: Balance;
|
|
20
|
+
priceUsd: number;
|
|
21
|
+
priceChange24h: number;
|
|
22
|
+
balanceInUsd: number;
|
|
20
23
|
}[];
|
|
21
24
|
}
|
|
22
25
|
export declare class MultiChainSavingsManager {
|
|
@@ -25,6 +28,9 @@ export declare class MultiChainSavingsManager {
|
|
|
25
28
|
private evmManagers;
|
|
26
29
|
private svmManagers;
|
|
27
30
|
private chainConfigs;
|
|
31
|
+
private getTokenKey;
|
|
32
|
+
private normalizePriceMap;
|
|
33
|
+
private getPriceMap;
|
|
28
34
|
constructor(mnemonic: string, chains: ChainConfig[], walletIndex?: number);
|
|
29
35
|
addChain(chain: ChainConfig): void;
|
|
30
36
|
removeChain(chainId: string): void;
|
|
@@ -3,12 +3,46 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MultiChainSavingsManager = void 0;
|
|
4
4
|
const evm_savings_1 = require("./evm-savings");
|
|
5
5
|
const svm_savings_1 = require("./svm-savings");
|
|
6
|
+
const price_1 = require("../price");
|
|
6
7
|
class MultiChainSavingsManager {
|
|
7
8
|
mnemonic;
|
|
8
9
|
walletIndex;
|
|
9
10
|
evmManagers = new Map();
|
|
10
11
|
svmManagers = new Map();
|
|
11
12
|
chainConfigs = new Map();
|
|
13
|
+
getTokenKey(tokenAddress, chainType) {
|
|
14
|
+
if (chainType === 'EVM') {
|
|
15
|
+
return tokenAddress.toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
return tokenAddress;
|
|
18
|
+
}
|
|
19
|
+
normalizePriceMap(prices, chainType) {
|
|
20
|
+
const priceMap = new Map();
|
|
21
|
+
for (const item of prices) {
|
|
22
|
+
if (!item?.tokenAddress || typeof item.price !== 'number')
|
|
23
|
+
continue;
|
|
24
|
+
priceMap.set(this.getTokenKey(item.tokenAddress, chainType), {
|
|
25
|
+
priceUsd: item.price,
|
|
26
|
+
priceChange24h: typeof item.priceChange24h === 'number'
|
|
27
|
+
? item.priceChange24h
|
|
28
|
+
: 0
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return priceMap;
|
|
32
|
+
}
|
|
33
|
+
async getPriceMap(chain, tokenAddresses) {
|
|
34
|
+
const chainWithId = chain.config;
|
|
35
|
+
if (typeof chainWithId.chainId !== 'number') {
|
|
36
|
+
return new Map();
|
|
37
|
+
}
|
|
38
|
+
const uniqueTokenAddresses = Array.from(new Set(tokenAddresses));
|
|
39
|
+
const priceResult = await (0, price_1.fetchPrices)({
|
|
40
|
+
vm: chain.type,
|
|
41
|
+
chainId: chainWithId.chainId,
|
|
42
|
+
tokenAddresses: uniqueTokenAddresses
|
|
43
|
+
});
|
|
44
|
+
return this.normalizePriceMap(priceResult.data?.prices ?? [], chain.type);
|
|
45
|
+
}
|
|
12
46
|
constructor(mnemonic, chains, walletIndex = 0) {
|
|
13
47
|
if (!mnemonic || typeof mnemonic !== 'string') {
|
|
14
48
|
throw new Error('Mnemonic must be a non-empty string');
|
|
@@ -96,14 +130,23 @@ class MultiChainSavingsManager {
|
|
|
96
130
|
const manager = this.evmManagers.get(chainId);
|
|
97
131
|
const balances = await manager.getPocketBalance(pocketIndex, tokens);
|
|
98
132
|
const pocket = manager.getPocket(pocketIndex);
|
|
133
|
+
const balanceRows = balances.map(b => ({
|
|
134
|
+
token: b.address === 'native' ? 'native' : b.address,
|
|
135
|
+
balance: b.balance
|
|
136
|
+
}));
|
|
137
|
+
const priceMap = await this.getPriceMap(chain, balanceRows.map(row => row.token));
|
|
99
138
|
return {
|
|
100
139
|
chainId,
|
|
101
140
|
chainType: 'EVM',
|
|
102
141
|
pocketIndex,
|
|
103
142
|
address: pocket.address,
|
|
104
|
-
balances:
|
|
105
|
-
token:
|
|
106
|
-
balance:
|
|
143
|
+
balances: balanceRows.map(row => ({
|
|
144
|
+
token: row.token,
|
|
145
|
+
balance: row.balance,
|
|
146
|
+
priceUsd: priceMap.get(this.getTokenKey(row.token, 'EVM'))?.priceUsd ?? 0,
|
|
147
|
+
priceChange24h: priceMap.get(this.getTokenKey(row.token, 'EVM'))?.priceChange24h ?? 0,
|
|
148
|
+
balanceInUsd: row.balance.formatted *
|
|
149
|
+
(priceMap.get(this.getTokenKey(row.token, 'EVM'))?.priceUsd ?? 0)
|
|
107
150
|
}))
|
|
108
151
|
};
|
|
109
152
|
}
|
|
@@ -111,14 +154,23 @@ class MultiChainSavingsManager {
|
|
|
111
154
|
const manager = this.svmManagers.get(chainId);
|
|
112
155
|
const balances = await manager.getPocketBalance(pocketIndex, tokens);
|
|
113
156
|
const pocket = manager.getPocket(pocketIndex);
|
|
157
|
+
const balanceRows = balances.map(b => ({
|
|
158
|
+
token: b.address === 'native' ? 'native' : b.address.toBase58(),
|
|
159
|
+
balance: b.balance
|
|
160
|
+
}));
|
|
161
|
+
const priceMap = await this.getPriceMap(chain, balanceRows.map(row => row.token));
|
|
114
162
|
return {
|
|
115
163
|
chainId,
|
|
116
164
|
chainType: 'SVM',
|
|
117
165
|
pocketIndex,
|
|
118
166
|
address: pocket.address.toBase58(),
|
|
119
|
-
balances:
|
|
120
|
-
token:
|
|
121
|
-
balance:
|
|
167
|
+
balances: balanceRows.map(row => ({
|
|
168
|
+
token: row.token,
|
|
169
|
+
balance: row.balance,
|
|
170
|
+
priceUsd: priceMap.get(this.getTokenKey(row.token, 'SVM'))?.priceUsd ?? 0,
|
|
171
|
+
priceChange24h: priceMap.get(this.getTokenKey(row.token, 'SVM'))?.priceChange24h ?? 0,
|
|
172
|
+
balanceInUsd: row.balance.formatted *
|
|
173
|
+
(priceMap.get(this.getTokenKey(row.token, 'SVM'))?.priceUsd ?? 0)
|
|
122
174
|
}))
|
|
123
175
|
};
|
|
124
176
|
}
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -47,18 +47,24 @@ const normalizePriceMap = (prices) => {
|
|
|
47
47
|
for (const item of prices) {
|
|
48
48
|
if (!item?.tokenAddress || typeof item.price !== "number")
|
|
49
49
|
continue;
|
|
50
|
-
priceMap.set(item.tokenAddress.toLowerCase(),
|
|
50
|
+
priceMap.set(item.tokenAddress.toLowerCase(), {
|
|
51
|
+
priceUsd: item.price,
|
|
52
|
+
priceChange24h: typeof item.priceChange24h === "number" ? item.priceChange24h : null,
|
|
53
|
+
});
|
|
51
54
|
}
|
|
52
55
|
return priceMap;
|
|
53
56
|
};
|
|
54
57
|
const enrichWithUsdValues = (items, priceMap) => {
|
|
55
58
|
return items.map((item) => {
|
|
56
59
|
const key = String(item.tokenAddress).toLowerCase();
|
|
57
|
-
const
|
|
60
|
+
const priceEntry = priceMap.get(key);
|
|
61
|
+
const priceUsd = priceEntry?.priceUsd ?? null;
|
|
62
|
+
const priceChange24h = priceEntry?.priceChange24h ?? null;
|
|
58
63
|
const valueUsd = priceUsd === null ? null : item.balanceFormatted * priceUsd;
|
|
59
64
|
return {
|
|
60
65
|
...item,
|
|
61
66
|
priceUsd,
|
|
67
|
+
priceChange24h,
|
|
62
68
|
valueUsd,
|
|
63
69
|
};
|
|
64
70
|
});
|
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ import { SVMSavingsManager } from "./svm-savings";
|
|
|
10
10
|
import { ChainWalletConfig, Balance } from "../types";
|
|
11
11
|
import { Hex } from "viem";
|
|
12
12
|
import { PublicKey } from "@solana/web3.js";
|
|
13
|
+
import { fetchPrices } from "../price";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Chain type identifier
|
|
@@ -46,6 +47,12 @@ export interface PocketBalance {
|
|
|
46
47
|
token: string;
|
|
47
48
|
/** Balance information */
|
|
48
49
|
balance: Balance;
|
|
50
|
+
/** Token unit price in USD (if unavailable, defaults to 0) */
|
|
51
|
+
priceUsd: number;
|
|
52
|
+
/** Token 24h price change percentage (if unavailable, defaults to 0) */
|
|
53
|
+
priceChange24h: number;
|
|
54
|
+
/** Balance in USD (if unavailable, defaults to 0) */
|
|
55
|
+
balanceInUsd: number;
|
|
49
56
|
}[];
|
|
50
57
|
}
|
|
51
58
|
|
|
@@ -90,6 +97,49 @@ export class MultiChainSavingsManager {
|
|
|
90
97
|
// Track chain configs
|
|
91
98
|
private chainConfigs: Map<string, ChainConfig> = new Map();
|
|
92
99
|
|
|
100
|
+
private getTokenKey(tokenAddress: string, chainType: ChainType): string {
|
|
101
|
+
if (chainType === 'EVM') {
|
|
102
|
+
return tokenAddress.toLowerCase();
|
|
103
|
+
}
|
|
104
|
+
return tokenAddress;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private normalizePriceMap(
|
|
108
|
+
prices: Array<{ tokenAddress: string; price: number; priceChange24h?: number }>,
|
|
109
|
+
chainType: ChainType
|
|
110
|
+
): Map<string, { priceUsd: number; priceChange24h: number }> {
|
|
111
|
+
const priceMap = new Map<string, { priceUsd: number; priceChange24h: number }>();
|
|
112
|
+
for (const item of prices) {
|
|
113
|
+
if (!item?.tokenAddress || typeof item.price !== 'number') continue;
|
|
114
|
+
priceMap.set(this.getTokenKey(item.tokenAddress, chainType), {
|
|
115
|
+
priceUsd: item.price,
|
|
116
|
+
priceChange24h: typeof item.priceChange24h === 'number'
|
|
117
|
+
? item.priceChange24h
|
|
118
|
+
: 0
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return priceMap;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async getPriceMap(
|
|
125
|
+
chain: ChainConfig,
|
|
126
|
+
tokenAddresses: string[]
|
|
127
|
+
): Promise<Map<string, { priceUsd: number; priceChange24h: number }>> {
|
|
128
|
+
const chainWithId = chain.config as Partial<ChainWalletConfig>;
|
|
129
|
+
if (typeof chainWithId.chainId !== 'number') {
|
|
130
|
+
return new Map();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const uniqueTokenAddresses = Array.from(new Set(tokenAddresses));
|
|
134
|
+
const priceResult = await fetchPrices({
|
|
135
|
+
vm: chain.type,
|
|
136
|
+
chainId: chainWithId.chainId,
|
|
137
|
+
tokenAddresses: uniqueTokenAddresses
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return this.normalizePriceMap(priceResult.data?.prices ?? [], chain.type);
|
|
141
|
+
}
|
|
142
|
+
|
|
93
143
|
/**
|
|
94
144
|
* Create a new MultiChainSavingsManager
|
|
95
145
|
*
|
|
@@ -253,30 +303,56 @@ export class MultiChainSavingsManager {
|
|
|
253
303
|
const manager = this.evmManagers.get(chainId)!;
|
|
254
304
|
const balances = await manager.getPocketBalance(pocketIndex, tokens);
|
|
255
305
|
const pocket = manager.getPocket(pocketIndex);
|
|
306
|
+
const balanceRows = balances.map(b => ({
|
|
307
|
+
token: b.address === 'native' ? 'native' : b.address,
|
|
308
|
+
balance: b.balance
|
|
309
|
+
}));
|
|
310
|
+
const priceMap = await this.getPriceMap(
|
|
311
|
+
chain,
|
|
312
|
+
balanceRows.map(row => row.token)
|
|
313
|
+
);
|
|
256
314
|
|
|
257
315
|
return {
|
|
258
316
|
chainId,
|
|
259
317
|
chainType: 'EVM',
|
|
260
318
|
pocketIndex,
|
|
261
319
|
address: pocket.address,
|
|
262
|
-
balances:
|
|
263
|
-
token:
|
|
264
|
-
balance:
|
|
320
|
+
balances: balanceRows.map(row => ({
|
|
321
|
+
token: row.token,
|
|
322
|
+
balance: row.balance,
|
|
323
|
+
priceUsd: priceMap.get(this.getTokenKey(row.token, 'EVM'))?.priceUsd ?? 0,
|
|
324
|
+
priceChange24h: priceMap.get(this.getTokenKey(row.token, 'EVM'))?.priceChange24h ?? 0,
|
|
325
|
+
balanceInUsd:
|
|
326
|
+
row.balance.formatted *
|
|
327
|
+
(priceMap.get(this.getTokenKey(row.token, 'EVM'))?.priceUsd ?? 0)
|
|
265
328
|
}))
|
|
266
329
|
};
|
|
267
330
|
} else {
|
|
268
331
|
const manager = this.svmManagers.get(chainId)!;
|
|
269
332
|
const balances = await manager.getPocketBalance(pocketIndex, tokens);
|
|
270
333
|
const pocket = manager.getPocket(pocketIndex);
|
|
334
|
+
const balanceRows = balances.map(b => ({
|
|
335
|
+
token: b.address === 'native' ? 'native' : b.address.toBase58(),
|
|
336
|
+
balance: b.balance
|
|
337
|
+
}));
|
|
338
|
+
const priceMap = await this.getPriceMap(
|
|
339
|
+
chain,
|
|
340
|
+
balanceRows.map(row => row.token)
|
|
341
|
+
);
|
|
271
342
|
|
|
272
343
|
return {
|
|
273
344
|
chainId,
|
|
274
345
|
chainType: 'SVM',
|
|
275
346
|
pocketIndex,
|
|
276
347
|
address: pocket.address.toBase58(),
|
|
277
|
-
balances:
|
|
278
|
-
token:
|
|
279
|
-
balance:
|
|
348
|
+
balances: balanceRows.map(row => ({
|
|
349
|
+
token: row.token,
|
|
350
|
+
balance: row.balance,
|
|
351
|
+
priceUsd: priceMap.get(this.getTokenKey(row.token, 'SVM'))?.priceUsd ?? 0,
|
|
352
|
+
priceChange24h: priceMap.get(this.getTokenKey(row.token, 'SVM'))?.priceChange24h ?? 0,
|
|
353
|
+
balanceInUsd:
|
|
354
|
+
row.balance.formatted *
|
|
355
|
+
(priceMap.get(this.getTokenKey(row.token, 'SVM'))?.priceUsd ?? 0)
|
|
280
356
|
}))
|
|
281
357
|
};
|
|
282
358
|
}
|
package/utils/utils.ts
CHANGED
|
@@ -33,6 +33,7 @@ export interface AddressPortfolioItem {
|
|
|
33
33
|
balanceRaw: string;
|
|
34
34
|
balanceFormatted: number;
|
|
35
35
|
priceUsd: number | null;
|
|
36
|
+
priceChange24h: number | null;
|
|
36
37
|
valueUsd: number | null;
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -71,27 +72,35 @@ export const detectVmTypeFromAddress = (address: string): vmTypes => {
|
|
|
71
72
|
}
|
|
72
73
|
};
|
|
73
74
|
|
|
74
|
-
const normalizePriceMap = (
|
|
75
|
-
|
|
75
|
+
const normalizePriceMap = (
|
|
76
|
+
prices: Array<{ tokenAddress: string; price: number; priceChange24h?: number }>
|
|
77
|
+
) => {
|
|
78
|
+
const priceMap = new Map<string, { priceUsd: number; priceChange24h: number | null }>();
|
|
76
79
|
for (const item of prices) {
|
|
77
80
|
if (!item?.tokenAddress || typeof item.price !== "number") continue;
|
|
78
|
-
priceMap.set(item.tokenAddress.toLowerCase(),
|
|
81
|
+
priceMap.set(item.tokenAddress.toLowerCase(), {
|
|
82
|
+
priceUsd: item.price,
|
|
83
|
+
priceChange24h: typeof item.priceChange24h === "number" ? item.priceChange24h : null,
|
|
84
|
+
});
|
|
79
85
|
}
|
|
80
86
|
return priceMap;
|
|
81
87
|
};
|
|
82
88
|
|
|
83
89
|
const enrichWithUsdValues = (
|
|
84
|
-
items: Omit<AddressPortfolioItem, "priceUsd" | "valueUsd">[],
|
|
85
|
-
priceMap: Map<string, number>
|
|
90
|
+
items: Omit<AddressPortfolioItem, "priceUsd" | "priceChange24h" | "valueUsd">[],
|
|
91
|
+
priceMap: Map<string, { priceUsd: number; priceChange24h: number | null }>
|
|
86
92
|
): AddressPortfolioResult["items"] => {
|
|
87
93
|
return items.map((item) => {
|
|
88
94
|
const key = String(item.tokenAddress).toLowerCase();
|
|
89
|
-
const
|
|
95
|
+
const priceEntry = priceMap.get(key);
|
|
96
|
+
const priceUsd = priceEntry?.priceUsd ?? null;
|
|
97
|
+
const priceChange24h = priceEntry?.priceChange24h ?? null;
|
|
90
98
|
const valueUsd = priceUsd === null ? null : item.balanceFormatted * priceUsd;
|
|
91
99
|
|
|
92
100
|
return {
|
|
93
101
|
...item,
|
|
94
102
|
priceUsd,
|
|
103
|
+
priceChange24h,
|
|
95
104
|
valueUsd,
|
|
96
105
|
};
|
|
97
106
|
});
|
|
@@ -129,7 +138,7 @@ export const getAddressPortfolioValue = async (params: AddressPortfolioParams):
|
|
|
129
138
|
if (vmType === "EVM") {
|
|
130
139
|
const client = createPublicClientFromChainConfig(chain);
|
|
131
140
|
|
|
132
|
-
const items: Omit<AddressPortfolioItem, "priceUsd" | "valueUsd">[] = [];
|
|
141
|
+
const items: Omit<AddressPortfolioItem, "priceUsd" | "priceChange24h" | "valueUsd">[] = [];
|
|
133
142
|
const priceTargets = new Set<string>();
|
|
134
143
|
|
|
135
144
|
if (includeNative) {
|
|
@@ -202,7 +211,7 @@ export const getAddressPortfolioValue = async (params: AddressPortfolioParams):
|
|
|
202
211
|
|
|
203
212
|
const svmAddress = new PublicKey(address);
|
|
204
213
|
const connection = new Connection(chain.rpcUrl);
|
|
205
|
-
const items: Omit<AddressPortfolioItem, "priceUsd" | "valueUsd">[] = [];
|
|
214
|
+
const items: Omit<AddressPortfolioItem, "priceUsd" | "priceChange24h" | "valueUsd">[] = [];
|
|
206
215
|
const priceTargets = new Set<string>();
|
|
207
216
|
|
|
208
217
|
if (includeNative) {
|