@pioneer-platform/pioneer-sdk 0.0.82 → 4.13.30

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.
@@ -0,0 +1,199 @@
1
+ import { ChartBalance, ChartParams, PortfolioBalance, PortfolioToken } from './types';
2
+ import { hydrateAssetData, checkDuplicateBalance, createBalanceIdentifier } from './utils';
3
+
4
+ const tag = '| charts-evm |';
5
+
6
+ export async function getEvmCharts(params: ChartParams): Promise<ChartBalance[]> {
7
+ const { blockchains, pioneer, pubkeys, context } = params;
8
+ const balances: ChartBalance[] = [];
9
+
10
+ // Find the primary address for portfolio lookup
11
+ // ONLY use EVM addresses since the portfolio endpoint uses Zapper which only supports Ethereum
12
+ const evmPubkey = pubkeys.find(
13
+ (e: any) => e.networks && Array.isArray(e.networks) && e.networks.includes('eip155:*'),
14
+ );
15
+ const primaryAddress = evmPubkey?.address || evmPubkey?.master;
16
+
17
+ console.log(tag, 'Total pubkeys available:', pubkeys.length);
18
+ console.log(tag, 'Blockchains to process:', blockchains);
19
+
20
+ // Only call portfolio endpoint if we have an EVM address (Zapper requirement)
21
+ if (!primaryAddress) {
22
+ console.log(
23
+ tag,
24
+ 'No EVM address found, skipping portfolio lookup (Zapper only supports Ethereum)',
25
+ );
26
+ return balances;
27
+ }
28
+
29
+ console.log(tag, 'Using EVM address for portfolio:', primaryAddress);
30
+
31
+ try {
32
+ let portfolio = await pioneer.GetPortfolio({ address: primaryAddress });
33
+ portfolio = portfolio.data;
34
+
35
+ if (!portfolio || !portfolio.balances) {
36
+ console.error(tag, 'No portfolio.balances found:', portfolio);
37
+ return balances;
38
+ }
39
+
40
+ // Process main balances
41
+ for (const balance of portfolio.balances) {
42
+ const processedBalance = processPortfolioBalance(balance, primaryAddress, context, blockchains);
43
+ if (processedBalance && !checkDuplicateBalance(balances, processedBalance.caip, processedBalance.pubkey)) {
44
+ balances.push(processedBalance);
45
+ }
46
+ }
47
+
48
+ // Process tokens from portfolio (if they exist)
49
+ if (portfolio.tokens && portfolio.tokens.length > 0) {
50
+ console.log(tag, 'Processing portfolio.tokens:', portfolio.tokens.length);
51
+
52
+ for (const token of portfolio.tokens) {
53
+ const processedToken = processPortfolioToken(token, primaryAddress, context, blockchains);
54
+ if (processedToken && !checkDuplicateBalance(balances, processedToken.caip, processedToken.pubkey)) {
55
+ balances.push(processedToken);
56
+ }
57
+ }
58
+ }
59
+ } catch (e) {
60
+ console.error(tag, 'Error fetching portfolio:', e);
61
+ }
62
+
63
+ return balances;
64
+ }
65
+
66
+ function processPortfolioBalance(
67
+ balance: PortfolioBalance,
68
+ primaryAddress: string,
69
+ context: string,
70
+ blockchains: string[]
71
+ ): ChartBalance | null {
72
+ if (!balance.caip) {
73
+ console.error(tag, 'No caip found for:', balance);
74
+ return null;
75
+ }
76
+
77
+ // Always derive networkId from CAIP
78
+ const networkId = balance.caip.split('/')[0];
79
+
80
+ // Skip if not in requested blockchains
81
+ if (!blockchains.includes(networkId)) {
82
+ return null;
83
+ }
84
+
85
+ // Hydrate with assetData
86
+ const assetInfo = hydrateAssetData(balance.caip);
87
+
88
+ // CRITICAL FIX: Use the original balance.pubkey from API if it exists,
89
+ // otherwise fall back to primaryAddress. This ensures identifier consistency
90
+ // with regular balance fetching which uses the API's balance.pubkey value.
91
+ const balancePubkey = balance.pubkey || primaryAddress;
92
+
93
+ // Determine if this is a token balance
94
+ const balanceType = assetInfo?.type || balance.type || 'native';
95
+ const isToken = balanceType !== 'native' && balance.caip.includes('/erc20:');
96
+
97
+ // Calculate price from valueUsd and balance if not provided
98
+ let calculatedPrice = balance.priceUsd || balance.price || 0;
99
+ if ((!calculatedPrice || calculatedPrice === 0) && balance.valueUsd && balance.balance) {
100
+ const balanceNum = parseFloat(balance.balance.toString());
101
+ const valueNum = parseFloat(balance.valueUsd.toString());
102
+ if (balanceNum > 0 && valueNum > 0) {
103
+ calculatedPrice = valueNum / balanceNum;
104
+ console.log(tag, `Calculated price from value/balance: ${calculatedPrice} for ${balance.caip}`);
105
+ }
106
+ }
107
+
108
+ const chartBalance: ChartBalance = {
109
+ context,
110
+ chart: 'pioneer',
111
+ contextType: 'keepkey',
112
+ name: assetInfo?.name || balance.name || 'Unknown',
113
+ caip: balance.caip,
114
+ icon: assetInfo?.icon || balance.icon || '',
115
+ pubkey: balancePubkey,
116
+ ticker: assetInfo?.symbol || balance.symbol || 'UNK',
117
+ ref: `${context}${balance.caip}`,
118
+ identifier: createBalanceIdentifier(balance.caip, balancePubkey),
119
+ networkId,
120
+ chain: networkId,
121
+ symbol: assetInfo?.symbol || balance.symbol || 'UNK',
122
+ type: balanceType,
123
+ token: isToken,
124
+ decimal: assetInfo?.decimal || balance.decimal,
125
+ balance: balance.balance.toString(),
126
+ price: calculatedPrice,
127
+ priceUsd: calculatedPrice,
128
+ valueUsd: balance.valueUsd.toString(),
129
+ updated: new Date().getTime(),
130
+ };
131
+
132
+ // Handle display icon for multi-asset icons
133
+ if (balance.display) {
134
+ chartBalance.icon = ['multi', chartBalance.icon, balance.display.toString()].toString();
135
+ }
136
+
137
+ return chartBalance;
138
+ }
139
+
140
+ function processPortfolioToken(
141
+ token: PortfolioToken,
142
+ primaryAddress: string,
143
+ context: string,
144
+ blockchains: string[]
145
+ ): ChartBalance | null {
146
+ if (!token.assetCaip || !token.networkId) {
147
+ return null;
148
+ }
149
+
150
+ // Extract the networkId from the assetCaip for special token formats
151
+ let extractedNetworkId = token.networkId;
152
+
153
+ // Special handling for tokens with special formats
154
+ if (token.assetCaip.includes('/denom:')) {
155
+ // Extract the network part before /denom:
156
+ const parts = token.assetCaip.split('/denom:');
157
+ if (parts.length > 0) {
158
+ extractedNetworkId = parts[0];
159
+ }
160
+ } else if (token.networkId && token.networkId.includes('/')) {
161
+ // For other tokens, split by / and take the first part
162
+ extractedNetworkId = token.networkId.split('/')[0];
163
+ }
164
+
165
+ // Skip if not in requested blockchains
166
+ if (!blockchains.includes(extractedNetworkId)) {
167
+ return null;
168
+ }
169
+
170
+ // Hydrate token with assetData
171
+ const tokenAssetInfo = hydrateAssetData(token.assetCaip);
172
+
173
+ // CRITICAL FIX: Use consistent pubkey for tokens too
174
+ const tokenPubkey = token.pubkey || primaryAddress;
175
+
176
+ const chartBalance: ChartBalance = {
177
+ context,
178
+ chart: 'pioneer',
179
+ contextType: context.split(':')[0],
180
+ name: tokenAssetInfo?.name || token.token?.coingeckoId || token.token?.name || 'Unknown',
181
+ caip: token.assetCaip,
182
+ icon: tokenAssetInfo?.icon || token.token?.icon || '',
183
+ pubkey: tokenPubkey,
184
+ ticker: tokenAssetInfo?.symbol || token.token?.symbol || 'UNK',
185
+ ref: `${context}${token.assetCaip}`,
186
+ identifier: createBalanceIdentifier(token.assetCaip, tokenPubkey),
187
+ networkId: extractedNetworkId,
188
+ symbol: tokenAssetInfo?.symbol || token.token?.symbol || 'UNK',
189
+ type: tokenAssetInfo?.type || 'token',
190
+ token: true, // Tokens from portfolio.tokens are always tokens
191
+ decimal: tokenAssetInfo?.decimal || token.token?.decimal,
192
+ balance: token.token?.balance?.toString() || '0',
193
+ priceUsd: token.token?.price || 0,
194
+ valueUsd: token.token?.balanceUSD || 0,
195
+ updated: new Date().getTime(),
196
+ };
197
+
198
+ return chartBalance;
199
+ }
@@ -0,0 +1,46 @@
1
+ import { ChartBalance, ChartParams } from './types';
2
+ import { getEvmCharts } from './evm';
3
+ import { getMayaCharts } from './maya';
4
+ import { getCosmosStakingCharts } from './cosmos-staking';
5
+
6
+ const tag = '| getCharts |';
7
+ const TIMEOUT = 30000;
8
+
9
+ export const getCharts = async (
10
+ blockchains: any,
11
+ pioneer: any,
12
+ pubkeys: any,
13
+ context: string
14
+ ): Promise<ChartBalance[]> => {
15
+ try {
16
+ const balances: ChartBalance[] = [];
17
+ console.log(tag, 'init');
18
+
19
+ const params: ChartParams = {
20
+ blockchains,
21
+ pioneer,
22
+ pubkeys,
23
+ context,
24
+ };
25
+
26
+ // Get EVM charts (includes portfolio and tokens)
27
+ const evmBalances = await getEvmCharts(params);
28
+ balances.push(...evmBalances);
29
+
30
+ // WORKAROUND: Check if MAYA tokens are missing and fetch them separately
31
+ const mayaBalances = await getMayaCharts(params, balances);
32
+ balances.push(...mayaBalances);
33
+
34
+ // Add Cosmos Staking Positions to charts
35
+ const stakingBalances = await getCosmosStakingCharts(params);
36
+ balances.push(...stakingBalances);
37
+
38
+ return balances;
39
+ } catch (error) {
40
+ console.error(tag, 'Error processing charts:', error);
41
+ throw error;
42
+ }
43
+ };
44
+
45
+ // Export types for external use
46
+ export * from './types';
@@ -0,0 +1,110 @@
1
+ import { ChartBalance, ChartParams } from './types';
2
+ import { hydrateAssetData, checkDuplicateBalance, createBalanceIdentifier } from './utils';
3
+
4
+ const tag = '| charts-maya |';
5
+
6
+ export async function getMayaCharts(
7
+ params: ChartParams,
8
+ existingBalances: ChartBalance[]
9
+ ): Promise<ChartBalance[]> {
10
+ const { blockchains, pioneer, pubkeys, context } = params;
11
+ const balances: ChartBalance[] = [];
12
+
13
+ try {
14
+ // Check if we have a MAYA address
15
+ const mayaPubkey = pubkeys.find(
16
+ (p: any) =>
17
+ p.networks &&
18
+ Array.isArray(p.networks) &&
19
+ p.networks.includes('cosmos:mayachain-mainnet-v1'),
20
+ );
21
+
22
+ if (!mayaPubkey || !mayaPubkey.address) {
23
+ console.log(tag, 'No MAYA pubkey found, skipping MAYA token fetch');
24
+ return balances;
25
+ }
26
+
27
+ // Check if MAYA network is in the requested blockchains
28
+ if (!blockchains.includes('cosmos:mayachain-mainnet-v1')) {
29
+ console.log(tag, 'MAYA network not in blockchains list, skipping MAYA token fetch');
30
+ return balances;
31
+ }
32
+
33
+ // Check if MAYA token is already in existing balances
34
+ const hasMayaToken = existingBalances.some(
35
+ (b: any) => b.caip === 'cosmos:mayachain-mainnet-v1/denom:maya',
36
+ );
37
+
38
+ if (hasMayaToken) {
39
+ console.log(tag, 'MAYA token already exists in balances, skipping');
40
+ return balances;
41
+ }
42
+
43
+ console.log(tag, 'MAYA token not found in portfolio, fetching separately...');
44
+ console.log(tag, 'MAYA pubkey address:', mayaPubkey.address);
45
+
46
+ // Try to get MAYA token balance via a separate call
47
+ // This is a workaround for the portfolio API not returning MAYA tokens
48
+ const mayaBalanceResponse = await pioneer.GetPortfolioBalances([
49
+ {
50
+ caip: 'cosmos:mayachain-mainnet-v1/denom:maya',
51
+ pubkey: mayaPubkey.address,
52
+ },
53
+ ]);
54
+
55
+ console.log(
56
+ tag,
57
+ 'MAYA balance response:',
58
+ JSON.stringify(mayaBalanceResponse?.data, null, 2),
59
+ );
60
+
61
+ if (!mayaBalanceResponse?.data || mayaBalanceResponse.data.length === 0) {
62
+ console.log(tag, 'No MAYA token balance returned from GetPortfolioBalances API');
63
+ return balances;
64
+ }
65
+
66
+ console.log(tag, 'Found MAYA token balances:', mayaBalanceResponse.data.length);
67
+
68
+ for (const mayaBalance of mayaBalanceResponse.data) {
69
+ if (mayaBalance.caip !== 'cosmos:mayachain-mainnet-v1/denom:maya') {
70
+ console.log(tag, 'Unexpected balance in MAYA response:', mayaBalance);
71
+ continue;
72
+ }
73
+
74
+ // Hydrate MAYA token with assetData
75
+ const mayaAssetInfo = hydrateAssetData(mayaBalance.caip);
76
+
77
+ // CACAO is the native asset, MAYA is a token on the Maya chain
78
+ const isToken = mayaBalance.caip.includes('/denom:') && !mayaBalance.caip.endsWith('/denom:cacao');
79
+
80
+ const mayaTokenBalance: ChartBalance = {
81
+ context,
82
+ chart: 'pioneer',
83
+ contextType: context.split(':')[0],
84
+ name: mayaAssetInfo?.name || 'Maya Token',
85
+ caip: mayaBalance.caip,
86
+ icon: mayaAssetInfo?.icon || 'https://pioneers.dev/coins/maya.png',
87
+ pubkey: mayaPubkey.address,
88
+ ticker: mayaAssetInfo?.symbol || 'MAYA',
89
+ ref: `${context}${mayaBalance.caip}`,
90
+ identifier: createBalanceIdentifier(mayaBalance.caip, mayaPubkey.address),
91
+ networkId: 'cosmos:mayachain-mainnet-v1',
92
+ symbol: mayaAssetInfo?.symbol || 'MAYA',
93
+ type: mayaAssetInfo?.type || 'token',
94
+ token: isToken, // MAYA is a token, CACAO is the native asset
95
+ decimal: mayaAssetInfo?.decimal,
96
+ balance: mayaBalance.balance?.toString() || '0',
97
+ priceUsd: parseFloat(mayaBalance.priceUsd) || 0,
98
+ valueUsd: parseFloat(mayaBalance.valueUsd) || 0,
99
+ updated: new Date().getTime(),
100
+ };
101
+
102
+ console.log(tag, 'Adding MAYA token to balances:', mayaTokenBalance);
103
+ balances.push(mayaTokenBalance);
104
+ }
105
+ } catch (mayaError) {
106
+ console.error(tag, 'Error fetching MAYA token balance:', mayaError);
107
+ }
108
+
109
+ return balances;
110
+ }
@@ -0,0 +1,77 @@
1
+ export interface ChartBalance {
2
+ context: string;
3
+ chart: string;
4
+ contextType: string;
5
+ name: string;
6
+ caip: string;
7
+ icon: string;
8
+ pubkey: string;
9
+ ticker: string;
10
+ ref: string;
11
+ identifier: string;
12
+ networkId: string;
13
+ chain?: string;
14
+ symbol: string;
15
+ type: string;
16
+ token?: boolean; // Indicates if this is a token balance
17
+ decimal?: number;
18
+ balance: string;
19
+ priceUsd: number;
20
+ valueUsd: number | string;
21
+ updated: number;
22
+ display?: string;
23
+ status?: string;
24
+ validator?: string;
25
+ }
26
+
27
+ export interface ChartParams {
28
+ blockchains: string[];
29
+ pioneer: any;
30
+ pubkeys: any[];
31
+ context: string;
32
+ }
33
+
34
+ export interface PortfolioBalance {
35
+ caip: string;
36
+ networkId?: string;
37
+ pubkey?: string;
38
+ balance: string | number;
39
+ valueUsd: string | number;
40
+ name?: string;
41
+ symbol?: string;
42
+ icon?: string;
43
+ decimal?: number;
44
+ type?: string;
45
+ display?: string;
46
+ }
47
+
48
+ export interface PortfolioToken {
49
+ assetCaip: string;
50
+ networkId?: string;
51
+ pubkey?: string;
52
+ token?: {
53
+ name?: string;
54
+ symbol?: string;
55
+ icon?: string;
56
+ coingeckoId?: string;
57
+ decimal?: number;
58
+ balance?: string | number;
59
+ price?: number;
60
+ balanceUSD?: number;
61
+ };
62
+ }
63
+
64
+ export interface StakingPosition {
65
+ caip: string;
66
+ balance: number;
67
+ priceUsd?: number;
68
+ valueUsd?: number;
69
+ name?: string;
70
+ icon?: string;
71
+ ticker?: string;
72
+ symbol?: string;
73
+ type?: string;
74
+ status?: string;
75
+ validatorAddress?: string;
76
+ validator?: string;
77
+ }
@@ -0,0 +1,24 @@
1
+ import { assetData } from '@pioneer-platform/pioneer-discovery';
2
+ import { ChartBalance } from './types';
3
+
4
+ export function hydrateAssetData(caip: string): any {
5
+ return assetData[caip] || assetData[caip.toLowerCase()];
6
+ }
7
+
8
+ export function checkDuplicateBalance(
9
+ balances: ChartBalance[],
10
+ caip: string,
11
+ pubkey: string,
12
+ validator?: string
13
+ ): boolean {
14
+ return balances.some(
15
+ (b) =>
16
+ b.caip === caip &&
17
+ b.pubkey === pubkey &&
18
+ (!validator || b.validator === validator)
19
+ );
20
+ }
21
+
22
+ export function createBalanceIdentifier(caip: string, pubkey: string): string {
23
+ return `${caip}:${pubkey}`;
24
+ }