helius-mcp 0.5.3 → 1.2.0
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 +52 -0
- package/LICENSE +1 -1
- package/README.md +97 -21
- package/dist/http.d.ts +1 -0
- package/dist/http.js +2 -0
- package/dist/index.js +93 -2
- package/dist/scripts/validate-catalog.d.ts +13 -0
- package/dist/scripts/validate-catalog.js +76 -0
- package/dist/tools/accounts.js +114 -204
- package/dist/tools/assets.js +109 -123
- package/dist/tools/auth.d.ts +2 -0
- package/dist/tools/auth.js +459 -0
- package/dist/tools/balance.js +28 -32
- package/dist/tools/blocks.js +68 -87
- package/dist/tools/config.js +18 -79
- package/dist/tools/das-extras.js +56 -41
- package/dist/tools/docs.js +12 -54
- package/dist/tools/enhanced-websockets.js +104 -74
- package/dist/tools/fees.js +42 -61
- package/dist/tools/guides.js +126 -515
- package/dist/tools/index.js +50 -2
- package/dist/tools/laserstream.js +107 -53
- package/dist/tools/network.js +47 -69
- package/dist/tools/plans.d.ts +21 -0
- package/dist/tools/plans.js +105 -246
- package/dist/tools/product-catalog.d.ts +10 -0
- package/dist/tools/product-catalog.js +123 -0
- package/dist/tools/recommend.d.ts +4 -0
- package/dist/tools/recommend.js +233 -0
- package/dist/tools/shared.js +8 -3
- package/dist/tools/solana-knowledge.d.ts +2 -0
- package/dist/tools/solana-knowledge.js +544 -0
- package/dist/tools/tokens.js +17 -18
- package/dist/tools/transactions.js +232 -302
- package/dist/tools/transfers.d.ts +2 -0
- package/dist/tools/transfers.js +270 -0
- package/dist/tools/wallet.js +175 -177
- package/dist/tools/webhooks.js +80 -82
- package/dist/types/transaction-types.d.ts +1 -1
- package/dist/types/transaction-types.js +2 -1
- package/dist/utils/config.d.ts +27 -0
- package/dist/utils/config.js +76 -0
- package/dist/utils/docs.d.ts +24 -0
- package/dist/utils/docs.js +72 -0
- package/dist/utils/errors.d.ts +32 -0
- package/dist/utils/errors.js +157 -0
- package/dist/utils/feedback.d.ts +16 -0
- package/dist/utils/feedback.js +87 -0
- package/dist/utils/formatters.d.ts +0 -1
- package/dist/utils/formatters.js +0 -3
- package/dist/utils/helius.d.ts +15 -5
- package/dist/utils/helius.js +52 -45
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +17 -7
- package/system-prompts/helius/claude.system.md +170 -0
- package/system-prompts/helius/full.md +2868 -0
- package/system-prompts/helius/openai.developer.md +170 -0
- package/system-prompts/helius-dflow/claude.system.md +290 -0
- package/system-prompts/helius-dflow/full.md +3647 -0
- package/system-prompts/helius-dflow/openai.developer.md +290 -0
- package/system-prompts/helius-phantom/claude.system.md +348 -0
- package/system-prompts/helius-phantom/full.md +5472 -0
- package/system-prompts/helius-phantom/openai.developer.md +348 -0
- package/system-prompts/svm/claude.system.md +174 -0
- package/system-prompts/svm/full.md +699 -0
- package/system-prompts/svm/openai.developer.md +174 -0
package/dist/tools/accounts.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { getHeliusClient, hasApiKey
|
|
2
|
+
import { getHeliusClient, hasApiKey } from '../utils/helius.js';
|
|
3
3
|
import { formatAddress, formatSol } from '../utils/formatters.js';
|
|
4
4
|
import { noApiKeyResponse } from './shared.js';
|
|
5
|
+
import { mcpText, validateEnum, handleToolError, addressError, paginationError } from '../utils/errors.js';
|
|
5
6
|
function formatParsedAccountData(account) {
|
|
6
7
|
const lines = [];
|
|
7
8
|
const parsedData = account.data;
|
|
@@ -20,145 +21,88 @@ function formatParsedAccountData(account) {
|
|
|
20
21
|
return lines;
|
|
21
22
|
}
|
|
22
23
|
export function registerAccountTools(server) {
|
|
23
|
-
// Get Account Info (single or batch) — uses
|
|
24
|
-
server.tool('getAccountInfo', 'Get detailed Solana account information for one or more accounts. For a single account: returns owner program, lamport balance, data size, executable status, and rent epoch. For batch: pass up to 100 addresses in "addresses" for fast bulk lookups. Use jsonParsed encoding (default) on token mint addresses to see Token-2022 extensions, authorities, and supply data. Use this to inspect any on-chain account.', {
|
|
24
|
+
// Get Account Info (single or batch) — uses SDK standard Solana RPC (Kit, bigint)
|
|
25
|
+
server.tool('getAccountInfo', 'BEST FOR: raw on-chain account inspection (owner program, data size, executable status, Token-2022 extensions). PREFER getAsset for token/NFT metadata. PREFER getBalance for SOL balance. Get detailed Solana account information for one or more accounts. For a single account: returns owner program, lamport balance, data size, executable status, and rent epoch. For batch: pass up to 100 addresses in "addresses" for fast bulk lookups. Use jsonParsed encoding (default) on token mint addresses to see Token-2022 extensions, authorities, and supply data. Use this to inspect any on-chain account. Credit cost: 1 credit (standard RPC).', {
|
|
25
26
|
address: z.string().optional().describe('Single account address (base58 encoded). Use this OR addresses, not both.'),
|
|
26
|
-
addresses: z.array(z.string()).optional().describe('Array of account addresses for batch lookup (up to 100). Use this OR address, not both.'),
|
|
27
|
-
encoding: z.
|
|
27
|
+
addresses: z.array(z.string()).optional().describe('Array of account addresses for batch lookup (base58 encoded, up to 100). Use this OR address, not both.'),
|
|
28
|
+
encoding: z.string().optional().default('jsonParsed').describe('Data encoding format')
|
|
28
29
|
}, async ({ address, addresses, encoding }) => {
|
|
29
30
|
if (!hasApiKey())
|
|
30
31
|
return noApiKeyResponse();
|
|
31
|
-
const
|
|
32
|
+
const err = validateEnum(encoding, ['base58', 'base64', 'jsonParsed'], 'Account Info Error', 'encoding');
|
|
33
|
+
if (err)
|
|
34
|
+
return err;
|
|
32
35
|
// Validate: must provide exactly one of address or addresses
|
|
33
36
|
if (!address && (!addresses || addresses.length === 0)) {
|
|
34
|
-
return
|
|
35
|
-
content: [{
|
|
36
|
-
type: 'text',
|
|
37
|
-
text: `**Error:** Provide either \`address\` (single account) or \`addresses\` (batch of up to 100).`
|
|
38
|
-
}]
|
|
39
|
-
};
|
|
37
|
+
return mcpText(`**Error:** Provide either \`address\` (single account) or \`addresses\` (batch of up to 100).`);
|
|
40
38
|
}
|
|
41
39
|
if (address && addresses && addresses.length > 0) {
|
|
42
|
-
return
|
|
43
|
-
content: [{
|
|
44
|
-
type: 'text',
|
|
45
|
-
text: `**Error:** Provide either \`address\` or \`addresses\`, not both.`
|
|
46
|
-
}]
|
|
47
|
-
};
|
|
40
|
+
return mcpText(`**Error:** Provide either \`address\` or \`addresses\`, not both.`);
|
|
48
41
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
text: `**Error:** Maximum 100 accounts per request. You provided ${addresses.length}.`
|
|
56
|
-
}]
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
const response = await fetch(url, {
|
|
60
|
-
method: 'POST',
|
|
61
|
-
headers: { 'Content-Type': 'application/json' },
|
|
62
|
-
body: JSON.stringify({
|
|
63
|
-
jsonrpc: '2.0',
|
|
64
|
-
id: 'get-multiple-accounts',
|
|
65
|
-
method: 'getMultipleAccounts',
|
|
66
|
-
params: [addresses, { encoding }]
|
|
67
|
-
})
|
|
68
|
-
});
|
|
69
|
-
const data = await response.json();
|
|
70
|
-
if (data.error) {
|
|
71
|
-
return {
|
|
72
|
-
content: [{
|
|
73
|
-
type: 'text',
|
|
74
|
-
text: `**Error**\n\n${data.error.message}`
|
|
75
|
-
}]
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
const accounts = data.result?.value || [];
|
|
79
|
-
const lines = [`**Multiple Accounts** (${addresses.length} requested)`, ''];
|
|
80
|
-
addresses.forEach((addr, i) => {
|
|
81
|
-
const account = accounts[i];
|
|
82
|
-
if (!account) {
|
|
83
|
-
lines.push(`**${formatAddress(addr)}:** Not found`);
|
|
42
|
+
try {
|
|
43
|
+
const helius = getHeliusClient();
|
|
44
|
+
// --- Batch mode ---
|
|
45
|
+
if (addresses && addresses.length > 0) {
|
|
46
|
+
if (addresses.length > 100) {
|
|
47
|
+
return mcpText(`**Error:** Maximum 100 accounts per request. You provided ${addresses.length}.`);
|
|
84
48
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (account
|
|
91
|
-
lines.push(
|
|
49
|
+
const result = await helius.getMultipleAccounts(addresses, { encoding });
|
|
50
|
+
const accounts = result?.value || [];
|
|
51
|
+
const lines = [`**Multiple Accounts** (${addresses.length} requested)`, ''];
|
|
52
|
+
addresses.forEach((addr, i) => {
|
|
53
|
+
const account = accounts[i];
|
|
54
|
+
if (!account) {
|
|
55
|
+
lines.push(`**${formatAddress(addr)}:** Not found`);
|
|
92
56
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
text: `**Account ${formatAddress(address)}**\n\nAccount not found or has no data.`
|
|
132
|
-
}]
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
const lines = [
|
|
136
|
-
`**Account ${formatAddress(address)}**`,
|
|
137
|
-
'',
|
|
138
|
-
`**Balance:** ${formatSol(account.lamports)} (${account.lamports.toLocaleString()} lamports)`,
|
|
139
|
-
`**Owner:** ${account.owner}`,
|
|
140
|
-
`**Executable:** ${account.executable ? 'Yes' : 'No'}`,
|
|
141
|
-
];
|
|
142
|
-
if (account.space !== undefined) {
|
|
143
|
-
lines.push(`**Data Size:** ${account.space} bytes`);
|
|
57
|
+
else {
|
|
58
|
+
lines.push(`**${formatAddress(addr)}**`);
|
|
59
|
+
lines.push(` Balance: ${formatSol(Number(account.lamports))}`);
|
|
60
|
+
lines.push(` Owner: ${account.owner}`);
|
|
61
|
+
lines.push(` Executable: ${account.executable ? 'Yes' : 'No'}`);
|
|
62
|
+
if (account.space !== undefined) {
|
|
63
|
+
lines.push(` Data Size: ${Number(account.space)} bytes`);
|
|
64
|
+
}
|
|
65
|
+
const parsedLines = formatParsedAccountData({ ...account, lamports: Number(account.lamports) });
|
|
66
|
+
lines.push(...parsedLines);
|
|
67
|
+
}
|
|
68
|
+
lines.push('');
|
|
69
|
+
});
|
|
70
|
+
return mcpText(lines.join('\n'));
|
|
71
|
+
}
|
|
72
|
+
// --- Single account mode ---
|
|
73
|
+
const result = await helius.getAccountInfo(address, { encoding });
|
|
74
|
+
const account = result?.value;
|
|
75
|
+
if (!account) {
|
|
76
|
+
return mcpText(`**Account ${formatAddress(address)}**\n\nAccount not found or has no data.`);
|
|
77
|
+
}
|
|
78
|
+
const lamports = Number(account.lamports);
|
|
79
|
+
const lines = [
|
|
80
|
+
`**Account ${formatAddress(address)}**`,
|
|
81
|
+
'',
|
|
82
|
+
`**Balance:** ${formatSol(lamports)} (${lamports.toLocaleString()} lamports)`,
|
|
83
|
+
`**Owner:** ${account.owner}`,
|
|
84
|
+
`**Executable:** ${account.executable ? 'Yes' : 'No'}`,
|
|
85
|
+
];
|
|
86
|
+
if (account.space !== undefined) {
|
|
87
|
+
lines.push(`**Data Size:** ${Number(account.space)} bytes`);
|
|
88
|
+
}
|
|
89
|
+
const parsedLines = formatParsedAccountData({ ...account, lamports });
|
|
90
|
+
if (parsedLines.length > 0) {
|
|
91
|
+
lines.push('', '**Parsed Data:**');
|
|
92
|
+
lines.push(...parsedLines);
|
|
93
|
+
}
|
|
94
|
+
return mcpText(lines.join('\n'));
|
|
144
95
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
lines.push(...parsedLines);
|
|
96
|
+
catch (err) {
|
|
97
|
+
return handleToolError(err, 'Error fetching account info', [
|
|
98
|
+
addressError('Account Info'),
|
|
99
|
+
]);
|
|
150
100
|
}
|
|
151
|
-
return {
|
|
152
|
-
content: [{
|
|
153
|
-
type: 'text',
|
|
154
|
-
text: lines.join('\n')
|
|
155
|
-
}]
|
|
156
|
-
};
|
|
157
101
|
});
|
|
158
102
|
// Get Token Accounts
|
|
159
|
-
server.tool('getTokenAccounts', 'Query token accounts with advanced filters. Can filter by mint address, owner address, or both. Returns token account addresses and balances.', {
|
|
160
|
-
owner: z.string().optional().describe('Filter by owner wallet address'),
|
|
161
|
-
mint: z.string().optional().describe('Filter by token mint address'),
|
|
103
|
+
server.tool('getTokenAccounts', 'BEST FOR: advanced token account queries with flexible filters (by mint, owner, or both). PREFER getTokenHolders for a quick top-holders list. PREFER getTokenBalances for a wallet\'s token holdings with prices. Query token accounts with advanced filters. Can filter by mint address, owner address, or both. Returns token account addresses and balances. Credit cost: 10 credits/call (DAS API).', {
|
|
104
|
+
owner: z.string().optional().describe('Filter by owner wallet address (base58 encoded)'),
|
|
105
|
+
mint: z.string().optional().describe('Filter by token mint address (base58 encoded)'),
|
|
162
106
|
page: z.number().optional().default(1).describe('Page number (starts at 1)'),
|
|
163
107
|
limit: z.number().optional().default(20).describe('Results per page (max 1000)')
|
|
164
108
|
}, async ({ owner, mint, page, limit }) => {
|
|
@@ -166,19 +110,23 @@ export function registerAccountTools(server) {
|
|
|
166
110
|
return noApiKeyResponse();
|
|
167
111
|
const helius = getHeliusClient();
|
|
168
112
|
if (!owner && !mint) {
|
|
169
|
-
return
|
|
170
|
-
content: [{
|
|
171
|
-
type: 'text',
|
|
172
|
-
text: `**Error:** You must provide at least one of: owner or mint address.`
|
|
173
|
-
}]
|
|
174
|
-
};
|
|
113
|
+
return mcpText(`**Error:** You must provide at least one of: owner or mint address.`);
|
|
175
114
|
}
|
|
176
115
|
const params = { page, limit };
|
|
177
116
|
if (owner)
|
|
178
117
|
params.owner = owner;
|
|
179
118
|
if (mint)
|
|
180
119
|
params.mint = mint;
|
|
181
|
-
|
|
120
|
+
let response;
|
|
121
|
+
try {
|
|
122
|
+
response = await helius.getTokenAccounts(params);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
return handleToolError(err, 'Error fetching token accounts', [
|
|
126
|
+
addressError('Token Accounts', 'Invalid Solana address. Please provide valid base58-encoded addresses for owner and/or mint.'),
|
|
127
|
+
paginationError('Token Accounts'),
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
182
130
|
const items = (response.token_accounts || []);
|
|
183
131
|
if (items.length === 0) {
|
|
184
132
|
const filterDesc = owner && mint
|
|
@@ -186,12 +134,7 @@ export function registerAccountTools(server) {
|
|
|
186
134
|
: owner
|
|
187
135
|
? `owner=${formatAddress(owner)}`
|
|
188
136
|
: `mint=${formatAddress(mint)}`;
|
|
189
|
-
return {
|
|
190
|
-
content: [{
|
|
191
|
-
type: 'text',
|
|
192
|
-
text: `**Token Accounts** (${filterDesc})\n\nNo token accounts found.`
|
|
193
|
-
}]
|
|
194
|
-
};
|
|
137
|
+
return mcpText(`**Token Accounts** (${filterDesc})\n\nNo token accounts found.`);
|
|
195
138
|
}
|
|
196
139
|
const lines = [`**Token Accounts** (${response.total || items.length} total, page ${page})`, ''];
|
|
197
140
|
items.forEach((account) => {
|
|
@@ -210,24 +153,22 @@ export function registerAccountTools(server) {
|
|
|
210
153
|
}
|
|
211
154
|
lines.push('');
|
|
212
155
|
});
|
|
213
|
-
return
|
|
214
|
-
content: [{
|
|
215
|
-
type: 'text',
|
|
216
|
-
text: lines.join('\n')
|
|
217
|
-
}]
|
|
218
|
-
};
|
|
156
|
+
return mcpText(lines.join('\n'));
|
|
219
157
|
});
|
|
220
|
-
// Get Program Accounts (V2 with pagination)
|
|
221
|
-
server.tool('getProgramAccounts', 'Get all accounts owned by a specific program. Returns account addresses, balances, and data sizes. Use dataSize to filter by account data length (e.g. 165 for token accounts). Useful for finding all accounts created by a program like a DEX, lending protocol, or custom program.', {
|
|
158
|
+
// Get Program Accounts (V2 with pagination) — uses SDK RpcCaller (no bigint)
|
|
159
|
+
server.tool('getProgramAccounts', 'BEST FOR: investigating protocol state — finding DEX pools, lending positions, or all accounts created by a specific program. PREFER searchAssets for NFT/token asset searches by creator or authority. Get all accounts owned by a specific program. Returns account addresses, balances, and data sizes. Use dataSize to filter by account data length (e.g. 165 for token accounts). Useful for finding all accounts created by a program like a DEX, lending protocol, or custom program. Credit cost: 10 credits/call.', {
|
|
222
160
|
programId: z.string().describe('Program ID (base58 encoded) — the owner program of the accounts to find'),
|
|
223
161
|
limit: z.number().optional().default(20).describe('Maximum accounts to return (default 20, max 100)'),
|
|
224
|
-
encoding: z.
|
|
162
|
+
encoding: z.string().optional().default('base64').describe('Data encoding format'),
|
|
225
163
|
dataSize: z.number().optional().describe('Filter by exact account data size in bytes (e.g. 165 for SPL token accounts)'),
|
|
226
164
|
paginationKey: z.string().optional().describe('Pagination cursor from a previous response to fetch the next page')
|
|
227
165
|
}, async ({ programId, limit, encoding, dataSize, paginationKey }) => {
|
|
228
166
|
if (!hasApiKey())
|
|
229
167
|
return noApiKeyResponse();
|
|
230
|
-
const
|
|
168
|
+
const encErr = validateEnum(encoding, ['base58', 'base64', 'jsonParsed'], 'Program Accounts Error', 'encoding');
|
|
169
|
+
if (encErr)
|
|
170
|
+
return encErr;
|
|
171
|
+
const helius = getHeliusClient();
|
|
231
172
|
const cappedLimit = Math.min(limit, 10_000);
|
|
232
173
|
const filters = [];
|
|
233
174
|
if (dataSize !== undefined) {
|
|
@@ -242,66 +183,35 @@ export function registerAccountTools(server) {
|
|
|
242
183
|
rpcParams.filters = filters;
|
|
243
184
|
if (paginationKey)
|
|
244
185
|
rpcParams.paginationKey = paginationKey;
|
|
245
|
-
const requestBody = {
|
|
246
|
-
jsonrpc: '2.0',
|
|
247
|
-
id: 'get-program-accounts-v2',
|
|
248
|
-
method: 'getProgramAccountsV2',
|
|
249
|
-
params: [programId, rpcParams]
|
|
250
|
-
};
|
|
251
|
-
let data;
|
|
252
186
|
try {
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
187
|
+
const data = await helius.getProgramAccountsV2([programId, rpcParams]);
|
|
188
|
+
// SDK returns result directly (or may wrap in RpcResponse with context/value)
|
|
189
|
+
const result = data.value ?? data;
|
|
190
|
+
const accounts = result?.accounts || [];
|
|
191
|
+
if (accounts.length === 0) {
|
|
192
|
+
return mcpText(`**Program Accounts for ${formatAddress(programId)}**\n\nNo accounts found.`);
|
|
193
|
+
}
|
|
194
|
+
const totalLabel = result?.totalResults
|
|
195
|
+
? `${result.totalResults.toLocaleString()} total`
|
|
196
|
+
: `${accounts.length} returned`;
|
|
197
|
+
const lines = [`**Program Accounts for ${formatAddress(programId)}** (${totalLabel})`, ''];
|
|
198
|
+
accounts.forEach((item) => {
|
|
199
|
+
lines.push(`- **${formatAddress(item.pubkey)}**`);
|
|
200
|
+
lines.push(` Balance: ${formatSol(item.account.lamports)}`);
|
|
201
|
+
if (item.account.space !== undefined) {
|
|
202
|
+
lines.push(` Data Size: ${item.account.space} bytes`);
|
|
203
|
+
}
|
|
204
|
+
lines.push(` Executable: ${item.account.executable ? 'Yes' : 'No'}`);
|
|
257
205
|
});
|
|
258
|
-
|
|
206
|
+
if (result?.paginationKey) {
|
|
207
|
+
lines.push('', `**Next Page:** Pass \`paginationKey: "${result.paginationKey}"\` to fetch the next page.`);
|
|
208
|
+
}
|
|
209
|
+
return mcpText(lines.join('\n'));
|
|
259
210
|
}
|
|
260
211
|
catch (err) {
|
|
261
|
-
return
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
text: `**Error fetching program accounts:** ${err instanceof Error ? err.message : String(err)}`
|
|
265
|
-
}]
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
if (data.error) {
|
|
269
|
-
return {
|
|
270
|
-
content: [{
|
|
271
|
-
type: 'text',
|
|
272
|
-
text: `**Error**\n\n${data.error.message}`
|
|
273
|
-
}]
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
const accounts = data.result?.accounts || [];
|
|
277
|
-
if (accounts.length === 0) {
|
|
278
|
-
return {
|
|
279
|
-
content: [{
|
|
280
|
-
type: 'text',
|
|
281
|
-
text: `**Program Accounts for ${formatAddress(programId)}**\n\nNo accounts found.`
|
|
282
|
-
}]
|
|
283
|
-
};
|
|
212
|
+
return handleToolError(err, 'Error fetching program accounts', [
|
|
213
|
+
addressError('Program Accounts'),
|
|
214
|
+
]);
|
|
284
215
|
}
|
|
285
|
-
const totalLabel = data.result?.totalResults
|
|
286
|
-
? `${data.result.totalResults.toLocaleString()} total`
|
|
287
|
-
: `${accounts.length} returned`;
|
|
288
|
-
const lines = [`**Program Accounts for ${formatAddress(programId)}** (${totalLabel})`, ''];
|
|
289
|
-
accounts.forEach((item) => {
|
|
290
|
-
lines.push(`- **${formatAddress(item.pubkey)}**`);
|
|
291
|
-
lines.push(` Balance: ${formatSol(item.account.lamports)}`);
|
|
292
|
-
if (item.account.space !== undefined) {
|
|
293
|
-
lines.push(` Data Size: ${item.account.space} bytes`);
|
|
294
|
-
}
|
|
295
|
-
lines.push(` Executable: ${item.account.executable ? 'Yes' : 'No'}`);
|
|
296
|
-
});
|
|
297
|
-
if (data.result?.paginationKey) {
|
|
298
|
-
lines.push('', `**Next Page:** Pass \`paginationKey: "${data.result.paginationKey}"\` to fetch the next page.`);
|
|
299
|
-
}
|
|
300
|
-
return {
|
|
301
|
-
content: [{
|
|
302
|
-
type: 'text',
|
|
303
|
-
text: lines.join('\n')
|
|
304
|
-
}]
|
|
305
|
-
};
|
|
306
216
|
});
|
|
307
217
|
}
|