@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: balances.map(b => ({
105
- token: b.address === 'native' ? 'native' : b.address,
106
- balance: b.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: balances.map(b => ({
120
- token: b.address === 'native' ? 'native' : b.address.toBase58(),
121
- balance: b.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
@@ -7,6 +7,7 @@ export interface AddressPortfolioItem {
7
7
  balanceRaw: string;
8
8
  balanceFormatted: number;
9
9
  priceUsd: number | null;
10
+ priceChange24h: number | null;
10
11
  valueUsd: number | null;
11
12
  }
12
13
  export interface AddressPortfolioResult {
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(), item.price);
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 priceUsd = priceMap.get(key) ?? null;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deserialize/multi-vm-wallet",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "devDependencies": {
5
5
  "@types/bn.js": "^5.2.0",
6
6
  "@types/crypto-js": "^4.2.2",
@@ -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: balances.map(b => ({
263
- token: b.address === 'native' ? 'native' : b.address,
264
- balance: b.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: balances.map(b => ({
278
- token: b.address === 'native' ? 'native' : b.address.toBase58(),
279
- balance: b.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 = (prices: Array<{ tokenAddress: string; price: number }>) => {
75
- const priceMap = new Map<string, number>();
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(), item.price);
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 priceUsd = priceMap.get(key) ?? null;
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) {