moltlaunch 2.0.0 → 2.0.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.
- package/README.md +2 -2
- package/dist/index.js +18 -18
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
- package/.claude/commands/deploy.md +0 -33
- package/.claude/hooks/regenerate-docs.sh +0 -12
- package/.claude/settings.json +0 -15
- package/.env.example +0 -2
- package/.github/workflows/deploy.yml +0 -37
- package/ROADMAP.md +0 -29
- package/contracts/MandateEscrowV4.sol +0 -281
- package/contracts/mocks/MockFlaunchBuyback.sol +0 -24
- package/hardhat.config.cjs +0 -29
- package/scripts/check-deploy-cost.ts +0 -15
- package/scripts/deploy-escrow-v4.ts +0 -81
- package/scripts/deploy-escrow.cjs +0 -22
- package/scripts/generate-docs.ts +0 -309
- package/shared/manifest.json +0 -87
- package/site/.vscode/extensions.json +0 -4
- package/site/.vscode/launch.json +0 -11
- package/site/README.md +0 -43
- package/site/astro.config.mjs +0 -21
- package/site/functions/agent/[[path]].ts +0 -9
- package/site/functions/task/[[path]].ts +0 -9
- package/site/index.html.bak +0 -1755
- package/site/package-lock.json +0 -6165
- package/site/package.json +0 -17
- package/site/public/_redirects +0 -1
- package/site/public/art/hero.webp +0 -0
- package/site/public/favicon.ico +0 -0
- package/site/public/favicon.svg +0 -4
- package/site/public/logo.png +0 -0
- package/site/public/skill.md +0 -276
- package/site/src/components/AgentGridCard.astro +0 -97
- package/site/src/components/AgentRow.astro +0 -75
- package/site/src/components/Footer.astro +0 -71
- package/site/src/components/GigCard.astro +0 -36
- package/site/src/components/Navbar.astro +0 -93
- package/site/src/components/ReviewCard.astro +0 -29
- package/site/src/components/SkillPill.astro +0 -19
- package/site/src/components/StatusBadge.astro +0 -27
- package/site/src/components/TaskEntry.astro +0 -98
- package/site/src/layouts/Layout.astro +0 -268
- package/site/src/lib/api.ts +0 -342
- package/site/src/pages/404.astro +0 -33
- package/site/src/pages/admin.astro +0 -445
- package/site/src/pages/agent/[...id].astro +0 -678
- package/site/src/pages/agents/index.astro +0 -235
- package/site/src/pages/dashboard.astro +0 -244
- package/site/src/pages/docs.astro +0 -191
- package/site/src/pages/how.astro +0 -156
- package/site/src/pages/index.astro +0 -226
- package/site/src/pages/leaderboard.astro +0 -155
- package/site/src/pages/task/[...id].astro +0 -1467
- package/site/src/styles/global.css +0 -159
- package/site/tailwind.config.mjs +0 -94
- package/site/tsconfig.json +0 -5
- package/site/wrangler.toml +0 -5
- package/src/commands/accept.ts +0 -135
- package/src/commands/agents.ts +0 -190
- package/src/commands/approve.ts +0 -127
- package/src/commands/claim.ts +0 -130
- package/src/commands/decline.ts +0 -55
- package/src/commands/dispute.ts +0 -92
- package/src/commands/earnings.ts +0 -86
- package/src/commands/feedback.ts +0 -147
- package/src/commands/gig.ts +0 -141
- package/src/commands/hire.ts +0 -96
- package/src/commands/inbox.ts +0 -135
- package/src/commands/message.ts +0 -97
- package/src/commands/profile.ts +0 -62
- package/src/commands/quote.ts +0 -80
- package/src/commands/refund.ts +0 -82
- package/src/commands/register.ts +0 -250
- package/src/commands/resolve.ts +0 -104
- package/src/commands/reviews.ts +0 -78
- package/src/commands/revise.ts +0 -65
- package/src/commands/submit.ts +0 -123
- package/src/commands/tasks.ts +0 -224
- package/src/commands/view.ts +0 -122
- package/src/commands/wallet.ts +0 -42
- package/src/index.ts +0 -285
- package/src/lib/agent0.ts +0 -158
- package/src/lib/auth.ts +0 -25
- package/src/lib/constants.ts +0 -55
- package/src/lib/escrow.ts +0 -374
- package/src/lib/files.ts +0 -87
- package/src/lib/flaunch.ts +0 -277
- package/src/lib/mandate.ts +0 -623
- package/src/lib/tasks.ts +0 -466
- package/src/lib/types.ts +0 -112
- package/src/lib/wallet.ts +0 -119
- package/src/lib/x402.ts +0 -86
- package/test/MandateEscrowV4.test.cjs +0 -568
- package/tsconfig.json +0 -19
- package/tsup.config.ts +0 -15
- package/worker/package-lock.json +0 -1812
- package/worker/package.json +0 -18
- package/worker/src/agents.ts +0 -755
- package/worker/src/auth.ts +0 -126
- package/worker/src/files.ts +0 -40
- package/worker/src/index.ts +0 -963
- package/worker/src/profiles.ts +0 -85
- package/worker/src/ratelimit.ts +0 -45
- package/worker/src/tasks.ts +0 -498
- package/worker/src/types.ts +0 -95
- package/worker/tsconfig.json +0 -15
- package/worker/wrangler.toml +0 -19
package/worker/src/agents.ts
DELETED
|
@@ -1,755 +0,0 @@
|
|
|
1
|
-
// ERC-8004 Agent Discovery
|
|
2
|
-
// Reads agents from the Identity Registry on Base
|
|
3
|
-
|
|
4
|
-
import type { Env } from './types';
|
|
5
|
-
|
|
6
|
-
// ERC-8004 Identity Registry on Base
|
|
7
|
-
const IDENTITY_REGISTRY = '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432';
|
|
8
|
-
const REPUTATION_REGISTRY = '0x8004BAa17C55a88189AE136b182e5fdA19dE9b63';
|
|
9
|
-
|
|
10
|
-
// MandateEscrow contracts - V3 for historical data, V4 for new escrows
|
|
11
|
-
const ESCROW_V3 = '0x893a388d253dc8509877b1b4529b7eaa17597b40';
|
|
12
|
-
const ESCROW_V4 = '0x2c46054b4577b4fcdde28cb613dc2ba4b1127b0c';
|
|
13
|
-
|
|
14
|
-
// Flaunch API for token market data
|
|
15
|
-
const FLAUNCH_API = 'https://api.flayerlabs.xyz/v1/base';
|
|
16
|
-
|
|
17
|
-
// Metadata keys stored in ERC-8004
|
|
18
|
-
const METADATA_KEYS = {
|
|
19
|
-
FLAUNCH_TOKEN: 'flaunchToken',
|
|
20
|
-
SKILLS: 'skills',
|
|
21
|
-
ENDPOINT: 'endpoint',
|
|
22
|
-
PRICE_WEI: 'priceWei',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// Agent type returned by the API
|
|
26
|
-
export interface Agent {
|
|
27
|
-
id: string; // agentId as hex string
|
|
28
|
-
agentIdBigInt: string; // Original bigint as string for display
|
|
29
|
-
owner: string;
|
|
30
|
-
agentURI: string;
|
|
31
|
-
agentWallet: string;
|
|
32
|
-
name: string;
|
|
33
|
-
description: string;
|
|
34
|
-
skills: string[];
|
|
35
|
-
endpoint: string;
|
|
36
|
-
priceWei: string;
|
|
37
|
-
flaunchToken?: string;
|
|
38
|
-
// Reputation from ERC-8004
|
|
39
|
-
reputation: {
|
|
40
|
-
count: number;
|
|
41
|
-
summaryValue: number;
|
|
42
|
-
summaryValueDecimals: number;
|
|
43
|
-
};
|
|
44
|
-
// Market data (Flaunch + DexScreener)
|
|
45
|
-
marketCapUSD?: number;
|
|
46
|
-
volume24hUSD?: number;
|
|
47
|
-
priceChange24h?: number;
|
|
48
|
-
liquidityUSD?: number;
|
|
49
|
-
holders?: number;
|
|
50
|
-
image?: string;
|
|
51
|
-
symbol?: string;
|
|
52
|
-
flaunchUrl?: string;
|
|
53
|
-
// Revenue tracking from escrow contract
|
|
54
|
-
totalBurnedETH?: number; // ETH used to buyback & burn tokens
|
|
55
|
-
totalBurnedUSD?: number;
|
|
56
|
-
totalBurnedTokens?: number; // Tokens sent to burn address
|
|
57
|
-
// Stats from worker (included in enriched responses)
|
|
58
|
-
completedTasks?: number;
|
|
59
|
-
totalEarningsETH?: number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Minimal ABI for reading from Identity Registry
|
|
63
|
-
const IDENTITY_ABI = {
|
|
64
|
-
ownerOf: 'function ownerOf(uint256 tokenId) view returns (address)',
|
|
65
|
-
tokenURI: 'function tokenURI(uint256 tokenId) view returns (string)',
|
|
66
|
-
getAgentWallet: 'function getAgentWallet(uint256 agentId) view returns (address)',
|
|
67
|
-
getMetadata: 'function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)',
|
|
68
|
-
balanceOf: 'function balanceOf(address owner) view returns (uint256)',
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const REPUTATION_ABI = {
|
|
72
|
-
getSummary: 'function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint64 count, int128 summaryValue, uint8 summaryValueDecimals)',
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Function selectors (keccak256 of signature, first 4 bytes)
|
|
76
|
-
const SELECTORS = {
|
|
77
|
-
ownerOf: '0x6352211e',
|
|
78
|
-
tokenURI: '0xc87b56dd',
|
|
79
|
-
getAgentWallet: '0xf3ea1f13', // getAgentWallet(uint256)
|
|
80
|
-
getMetadata: '0xcb4799f2', // getMetadata(uint256,string)
|
|
81
|
-
getSummary: '0x81bbba58', // getSummary(uint256,address[],string,string)
|
|
82
|
-
getClients: '0x42dd519c', // getClients(uint256)
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Encode uint256 for RPC call
|
|
86
|
-
function encodeUint256(value: bigint): string {
|
|
87
|
-
return value.toString(16).padStart(64, '0');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Encode string for RPC call (offset + length + data)
|
|
91
|
-
function encodeString(str: string): string {
|
|
92
|
-
const encoder = new TextEncoder();
|
|
93
|
-
const bytes = encoder.encode(str);
|
|
94
|
-
let hex = '';
|
|
95
|
-
for (const b of bytes) {
|
|
96
|
-
hex += b.toString(16).padStart(2, '0');
|
|
97
|
-
}
|
|
98
|
-
const len = bytes.length.toString(16).padStart(64, '0');
|
|
99
|
-
const padded = hex.padEnd(Math.ceil(hex.length / 64) * 64, '0');
|
|
100
|
-
return len + padded;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Decode hex string to UTF-8
|
|
104
|
-
function decodeString(hex: string): string {
|
|
105
|
-
if (!hex || hex === '0x' || hex === '') return '';
|
|
106
|
-
const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
107
|
-
if (clean.length === 0) return '';
|
|
108
|
-
const bytes = new Uint8Array(clean.length / 2);
|
|
109
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
110
|
-
bytes[i] = parseInt(clean.substring(i * 2, i * 2 + 2), 16);
|
|
111
|
-
}
|
|
112
|
-
const decoder = new TextDecoder('utf-8');
|
|
113
|
-
return decoder.decode(bytes).replace(/\0/g, '');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Decode hex to bigint
|
|
117
|
-
function decodeBigInt(hex: string): bigint {
|
|
118
|
-
if (!hex || hex === '0x' || hex === '') return 0n;
|
|
119
|
-
const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
120
|
-
if (clean.length === 0) return 0n;
|
|
121
|
-
return BigInt('0x' + clean);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Make eth_call
|
|
125
|
-
async function ethCall(rpcUrl: string, to: string, data: string): Promise<string> {
|
|
126
|
-
console.log('[ethCall] Calling:', to, data.substring(0, 20) + '...');
|
|
127
|
-
const res = await fetch(rpcUrl, {
|
|
128
|
-
method: 'POST',
|
|
129
|
-
headers: { 'Content-Type': 'application/json' },
|
|
130
|
-
body: JSON.stringify({
|
|
131
|
-
jsonrpc: '2.0',
|
|
132
|
-
method: 'eth_call',
|
|
133
|
-
params: [{ to, data }, 'latest'],
|
|
134
|
-
id: 1,
|
|
135
|
-
}),
|
|
136
|
-
});
|
|
137
|
-
const json = await res.json() as { result?: string; error?: { message: string } };
|
|
138
|
-
console.log('[ethCall] Response:', json.error ? 'ERROR: ' + json.error.message : 'OK');
|
|
139
|
-
if (json.error) throw new Error(json.error.message);
|
|
140
|
-
return json.result || '0x';
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Get ETH balance of an address
|
|
144
|
-
async function getBalance(rpcUrl: string, address: string): Promise<number> {
|
|
145
|
-
try {
|
|
146
|
-
const res = await fetch(rpcUrl, {
|
|
147
|
-
method: 'POST',
|
|
148
|
-
headers: { 'Content-Type': 'application/json' },
|
|
149
|
-
body: JSON.stringify({
|
|
150
|
-
jsonrpc: '2.0',
|
|
151
|
-
method: 'eth_getBalance',
|
|
152
|
-
params: [address, 'latest'],
|
|
153
|
-
id: 1,
|
|
154
|
-
}),
|
|
155
|
-
});
|
|
156
|
-
const json = await res.json() as { result?: string; error?: { message: string } };
|
|
157
|
-
if (json.error || !json.result) return 0;
|
|
158
|
-
return Number(BigInt(json.result)) / 1e18;
|
|
159
|
-
} catch {
|
|
160
|
-
return 0;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Get total ETH burned for a token from escrow BuybackBurned events
|
|
165
|
-
// Event: BuybackBurned(bytes32 indexed taskId, address indexed token, uint256 ethAmount)
|
|
166
|
-
async function getTotalBurned(rpcUrl: string, tokenAddress: string): Promise<number> {
|
|
167
|
-
const topic0 = '0xd348d018935f8254a4d4a03b887cc0f0a0cdd7c6afd4414feb31b6af57c95bbc';
|
|
168
|
-
const topic2 = '0x000000000000000000000000' + tokenAddress.slice(2).toLowerCase();
|
|
169
|
-
|
|
170
|
-
let totalWei = 0n;
|
|
171
|
-
|
|
172
|
-
// Query from both V3 and V4 contracts
|
|
173
|
-
for (const contractAddr of [ESCROW_V3, ESCROW_V4]) {
|
|
174
|
-
try {
|
|
175
|
-
const res = await fetch(rpcUrl, {
|
|
176
|
-
method: 'POST',
|
|
177
|
-
headers: { 'Content-Type': 'application/json' },
|
|
178
|
-
body: JSON.stringify({
|
|
179
|
-
jsonrpc: '2.0',
|
|
180
|
-
method: 'eth_getLogs',
|
|
181
|
-
params: [{
|
|
182
|
-
address: contractAddr,
|
|
183
|
-
topics: [topic0, null, topic2],
|
|
184
|
-
fromBlock: '0x0',
|
|
185
|
-
toBlock: 'latest',
|
|
186
|
-
}],
|
|
187
|
-
id: 1,
|
|
188
|
-
}),
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const json = await res.json() as {
|
|
192
|
-
result?: Array<{ data: string }>;
|
|
193
|
-
error?: { message: string };
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
if (json.error || !json.result) continue;
|
|
197
|
-
|
|
198
|
-
for (const log of json.result) {
|
|
199
|
-
const amount = BigInt(log.data);
|
|
200
|
-
totalWei += amount;
|
|
201
|
-
}
|
|
202
|
-
} catch {
|
|
203
|
-
// Continue with other contract if one fails
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return Number(totalWei) / 1e18;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Get total tokens sent to burn address (0xdead) for a token
|
|
211
|
-
// Uses Transfer(from, to, value) events where to=0xdead
|
|
212
|
-
async function getTotalTokensBurned(rpcUrl: string, tokenAddress: string): Promise<number> {
|
|
213
|
-
// keccak256("Transfer(address,address,uint256)")
|
|
214
|
-
const topic0 = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
|
|
215
|
-
const deadTopic = '0x000000000000000000000000000000000000000000000000000000000000dead';
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
const res = await fetch(rpcUrl, {
|
|
219
|
-
method: 'POST',
|
|
220
|
-
headers: { 'Content-Type': 'application/json' },
|
|
221
|
-
body: JSON.stringify({
|
|
222
|
-
jsonrpc: '2.0',
|
|
223
|
-
method: 'eth_getLogs',
|
|
224
|
-
params: [{
|
|
225
|
-
address: tokenAddress,
|
|
226
|
-
topics: [topic0, null, deadTopic],
|
|
227
|
-
fromBlock: '0x0',
|
|
228
|
-
toBlock: 'latest',
|
|
229
|
-
}],
|
|
230
|
-
id: 1,
|
|
231
|
-
}),
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
const json = await res.json() as {
|
|
235
|
-
result?: Array<{ data: string }>;
|
|
236
|
-
error?: { message: string };
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
if (json.error || !json.result) return 0;
|
|
240
|
-
|
|
241
|
-
let totalWei = 0n;
|
|
242
|
-
for (const log of json.result) {
|
|
243
|
-
totalWei += BigInt(log.data);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return Number(totalWei) / 1e18;
|
|
247
|
-
} catch {
|
|
248
|
-
return 0;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Get logs for Registered events with pagination
|
|
253
|
-
async function getRegisteredEvents(rpcUrl: string): Promise<Array<{ agentId: bigint; owner: string }>> {
|
|
254
|
-
// Topic0 for Registered event from ERC-8004
|
|
255
|
-
const topic0 = '0xca52e62c367d81bb2e328eb795f7c7ba24afb478408a26c0e201d155c449bc4a';
|
|
256
|
-
|
|
257
|
-
// Query in chunks to avoid RPC limits (max 10000 logs per query)
|
|
258
|
-
const allLogs: Array<{ agentId: bigint; owner: string }> = [];
|
|
259
|
-
|
|
260
|
-
// Get current block number
|
|
261
|
-
const blockRes = await fetch(rpcUrl, {
|
|
262
|
-
method: 'POST',
|
|
263
|
-
headers: { 'Content-Type': 'application/json' },
|
|
264
|
-
body: JSON.stringify({
|
|
265
|
-
jsonrpc: '2.0',
|
|
266
|
-
method: 'eth_blockNumber',
|
|
267
|
-
params: [],
|
|
268
|
-
id: 1,
|
|
269
|
-
}),
|
|
270
|
-
});
|
|
271
|
-
const blockJson = await blockRes.json() as { result?: string };
|
|
272
|
-
const latestBlock = parseInt(blockJson.result || '0x0', 16);
|
|
273
|
-
|
|
274
|
-
// Query in chunks of 100000 blocks
|
|
275
|
-
const chunkSize = 100000;
|
|
276
|
-
let fromBlock = 0;
|
|
277
|
-
|
|
278
|
-
while (fromBlock < latestBlock) {
|
|
279
|
-
const toBlock = Math.min(fromBlock + chunkSize - 1, latestBlock);
|
|
280
|
-
|
|
281
|
-
const res = await fetch(rpcUrl, {
|
|
282
|
-
method: 'POST',
|
|
283
|
-
headers: { 'Content-Type': 'application/json' },
|
|
284
|
-
body: JSON.stringify({
|
|
285
|
-
jsonrpc: '2.0',
|
|
286
|
-
method: 'eth_getLogs',
|
|
287
|
-
params: [{
|
|
288
|
-
address: IDENTITY_REGISTRY,
|
|
289
|
-
topics: [topic0],
|
|
290
|
-
fromBlock: '0x' + fromBlock.toString(16),
|
|
291
|
-
toBlock: '0x' + toBlock.toString(16),
|
|
292
|
-
}],
|
|
293
|
-
id: 1,
|
|
294
|
-
}),
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
const json = await res.json() as {
|
|
298
|
-
result?: Array<{ topics: string[]; data: string }>;
|
|
299
|
-
error?: { message: string };
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
if (json.error) {
|
|
303
|
-
console.log(`[getRegisteredEvents] Error querying blocks ${fromBlock}-${toBlock}:`, json.error.message);
|
|
304
|
-
// If we get an error (like too many results), try smaller chunks
|
|
305
|
-
if (json.error.message.includes('exceed') || json.error.message.includes('limit')) {
|
|
306
|
-
// Skip this range for now
|
|
307
|
-
fromBlock = toBlock + 1;
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
throw new Error(json.error.message);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const logs = json.result || [];
|
|
314
|
-
console.log(`[getRegisteredEvents] Found ${logs.length} events in blocks ${fromBlock}-${toBlock}`);
|
|
315
|
-
|
|
316
|
-
for (const log of logs) {
|
|
317
|
-
allLogs.push({
|
|
318
|
-
agentId: BigInt(log.topics[1]),
|
|
319
|
-
owner: '0x' + log.topics[2].slice(-40),
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
fromBlock = toBlock + 1;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
console.log(`[getRegisteredEvents] Total events: ${allLogs.length}`);
|
|
327
|
-
return allLogs;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Get agent details from ERC-8004
|
|
331
|
-
async function getAgentFromContract(rpcUrl: string, agentId: bigint): Promise<Agent | null> {
|
|
332
|
-
console.log('[getAgentFromContract] Starting for agentId:', agentId.toString());
|
|
333
|
-
try {
|
|
334
|
-
const agentIdHex = encodeUint256(agentId);
|
|
335
|
-
console.log('[getAgentFromContract] agentIdHex:', agentIdHex);
|
|
336
|
-
|
|
337
|
-
// Get owner
|
|
338
|
-
const ownerData = SELECTORS.ownerOf + agentIdHex;
|
|
339
|
-
console.log('[getAgentFromContract] Calling ownerOf...');
|
|
340
|
-
const ownerResult = await ethCall(rpcUrl, IDENTITY_REGISTRY, ownerData);
|
|
341
|
-
console.log('[getAgentFromContract] ownerResult:', ownerResult);
|
|
342
|
-
const owner = '0x' + ownerResult.slice(-40);
|
|
343
|
-
console.log('[getAgentFromContract] owner:', owner);
|
|
344
|
-
|
|
345
|
-
// Get tokenURI
|
|
346
|
-
const uriData = SELECTORS.tokenURI + agentIdHex;
|
|
347
|
-
const uriResult = await ethCall(rpcUrl, IDENTITY_REGISTRY, uriData);
|
|
348
|
-
// Decode ABI-encoded string response
|
|
349
|
-
let agentURI = '';
|
|
350
|
-
if (uriResult && uriResult.length > 130) {
|
|
351
|
-
const strOffset = parseInt(uriResult.slice(2, 66), 16) * 2;
|
|
352
|
-
const strLen = parseInt(uriResult.slice(66, 130), 16);
|
|
353
|
-
agentURI = decodeString(uriResult.slice(130, 130 + strLen * 2));
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Get agent wallet (optional - may not be set)
|
|
357
|
-
let agentWallet = owner;
|
|
358
|
-
try {
|
|
359
|
-
const walletData = SELECTORS.getAgentWallet + agentIdHex;
|
|
360
|
-
const walletResult = await ethCall(rpcUrl, IDENTITY_REGISTRY, walletData);
|
|
361
|
-
if (walletResult && walletResult !== '0x' + '0'.repeat(64)) {
|
|
362
|
-
agentWallet = '0x' + walletResult.slice(-40);
|
|
363
|
-
}
|
|
364
|
-
} catch {
|
|
365
|
-
// Agent wallet not set, use owner
|
|
366
|
-
console.log('[getAgentFromContract] No agentWallet set, using owner');
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// Get metadata - skills (optional)
|
|
370
|
-
let skills: string[] = [];
|
|
371
|
-
try {
|
|
372
|
-
const skillsKey = encodeString(METADATA_KEYS.SKILLS);
|
|
373
|
-
const skillsData = SELECTORS.getMetadata + agentIdHex + '0000000000000000000000000000000000000000000000000000000000000040' + skillsKey;
|
|
374
|
-
const skillsResult = await ethCall(rpcUrl, IDENTITY_REGISTRY, skillsData);
|
|
375
|
-
if (skillsResult && skillsResult.length > 130) {
|
|
376
|
-
const strLen = parseInt(skillsResult.slice(66, 130), 16);
|
|
377
|
-
const skillsStr = decodeString(skillsResult.slice(130, 130 + strLen * 2));
|
|
378
|
-
skills = skillsStr.split(',').filter(Boolean);
|
|
379
|
-
}
|
|
380
|
-
} catch {
|
|
381
|
-
console.log('[getAgentFromContract] No skills metadata');
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Get metadata - endpoint (optional)
|
|
385
|
-
let endpoint = '';
|
|
386
|
-
try {
|
|
387
|
-
const endpointKey = encodeString(METADATA_KEYS.ENDPOINT);
|
|
388
|
-
const endpointData = SELECTORS.getMetadata + agentIdHex + '0000000000000000000000000000000000000000000000000000000000000040' + endpointKey;
|
|
389
|
-
const endpointResult = await ethCall(rpcUrl, IDENTITY_REGISTRY, endpointData);
|
|
390
|
-
if (endpointResult && endpointResult.length > 130) {
|
|
391
|
-
const strLen = parseInt(endpointResult.slice(66, 130), 16);
|
|
392
|
-
endpoint = decodeString(endpointResult.slice(130, 130 + strLen * 2));
|
|
393
|
-
}
|
|
394
|
-
} catch {
|
|
395
|
-
console.log('[getAgentFromContract] No endpoint metadata');
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Get metadata - priceWei (optional)
|
|
399
|
-
let priceWei = '0';
|
|
400
|
-
try {
|
|
401
|
-
const priceKey = encodeString(METADATA_KEYS.PRICE_WEI);
|
|
402
|
-
const priceData = SELECTORS.getMetadata + agentIdHex + '0000000000000000000000000000000000000000000000000000000000000040' + priceKey;
|
|
403
|
-
const priceResult = await ethCall(rpcUrl, IDENTITY_REGISTRY, priceData);
|
|
404
|
-
// bytes response: offset (32 bytes) + length (32 bytes) + data
|
|
405
|
-
// priceWei is stored as 32-byte bigint
|
|
406
|
-
if (priceResult && priceResult.length >= 194) {
|
|
407
|
-
const dataLen = parseInt(priceResult.slice(66, 130), 16);
|
|
408
|
-
if (dataLen === 32) {
|
|
409
|
-
// Extract the 32-byte bigint from data section
|
|
410
|
-
const priceHex = priceResult.slice(130, 194);
|
|
411
|
-
priceWei = decodeBigInt('0x' + priceHex).toString();
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
} catch {
|
|
415
|
-
console.log('[getAgentFromContract] No priceWei metadata');
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Get metadata - flaunchToken (optional)
|
|
419
|
-
let flaunchToken: string | undefined;
|
|
420
|
-
try {
|
|
421
|
-
const flaunchKey = encodeString(METADATA_KEYS.FLAUNCH_TOKEN);
|
|
422
|
-
const flaunchData = SELECTORS.getMetadata + agentIdHex + '0000000000000000000000000000000000000000000000000000000000000040' + flaunchKey;
|
|
423
|
-
const flaunchResult = await ethCall(rpcUrl, IDENTITY_REGISTRY, flaunchData);
|
|
424
|
-
// bytes response: offset (32 bytes) + length (32 bytes) + data (padded)
|
|
425
|
-
// For address: length=20, data starts at position 130 (after 0x + 64 + 64)
|
|
426
|
-
if (flaunchResult && flaunchResult.length >= 194) {
|
|
427
|
-
const dataLen = parseInt(flaunchResult.slice(66, 130), 16);
|
|
428
|
-
if (dataLen === 20) {
|
|
429
|
-
// It's an address - read first 40 hex chars of data (not last!)
|
|
430
|
-
const tokenAddr = '0x' + flaunchResult.slice(130, 170).toLowerCase();
|
|
431
|
-
if (tokenAddr !== '0x0000000000000000000000000000000000000000') {
|
|
432
|
-
flaunchToken = tokenAddr;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
} catch (e) {
|
|
437
|
-
console.log('[getAgentFromContract] No flaunchToken metadata:', e);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Get reputation from Reputation Registry
|
|
441
|
-
// Step 1: getClients(agentId) to get addresses who left feedback
|
|
442
|
-
// Step 2: getSummary(agentId, clients, '', '') with those addresses
|
|
443
|
-
let reputation = { count: 0, summaryValue: 0, summaryValueDecimals: 0 };
|
|
444
|
-
try {
|
|
445
|
-
const clientsData = SELECTORS.getClients + agentIdHex;
|
|
446
|
-
const clientsResult = await ethCall(rpcUrl, REPUTATION_REGISTRY, clientsData);
|
|
447
|
-
|
|
448
|
-
// Decode address[] response: offset (32) + length (32) + addresses (32 each)
|
|
449
|
-
let clients: string[] = [];
|
|
450
|
-
if (clientsResult && clientsResult.length > 130) {
|
|
451
|
-
const arrLen = parseInt(clientsResult.slice(66, 130), 16);
|
|
452
|
-
for (let i = 0; i < arrLen; i++) {
|
|
453
|
-
const start = 130 + i * 64;
|
|
454
|
-
clients.push(clientsResult.slice(start + 24, start + 64)); // last 20 bytes
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
if (clients.length > 0) {
|
|
459
|
-
// Build getSummary calldata with client addresses
|
|
460
|
-
// Head: agentId + offset_addr[] + offset_tag1 + offset_tag2
|
|
461
|
-
const addrArrayOffset = (4 * 32).toString(16).padStart(64, '0'); // 0x80
|
|
462
|
-
const addrDataSize = (1 + clients.length) * 32; // length word + address words
|
|
463
|
-
const tag1Offset = (4 * 32 + addrDataSize).toString(16).padStart(64, '0');
|
|
464
|
-
const tag2Offset = (4 * 32 + addrDataSize + 32).toString(16).padStart(64, '0');
|
|
465
|
-
|
|
466
|
-
let summaryData = SELECTORS.getSummary + agentIdHex +
|
|
467
|
-
addrArrayOffset + tag1Offset + tag2Offset;
|
|
468
|
-
|
|
469
|
-
// Address array: length + each address padded to 32 bytes
|
|
470
|
-
summaryData += clients.length.toString(16).padStart(64, '0');
|
|
471
|
-
for (const addr of clients) {
|
|
472
|
-
summaryData += addr.padStart(64, '0');
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Empty tag1 and tag2
|
|
476
|
-
summaryData += '0000000000000000000000000000000000000000000000000000000000000000';
|
|
477
|
-
summaryData += '0000000000000000000000000000000000000000000000000000000000000000';
|
|
478
|
-
|
|
479
|
-
const repResult = await ethCall(rpcUrl, REPUTATION_REGISTRY, summaryData);
|
|
480
|
-
if (repResult && repResult.length >= 194) {
|
|
481
|
-
reputation = {
|
|
482
|
-
count: parseInt(repResult.slice(2, 66), 16),
|
|
483
|
-
summaryValue: parseInt(repResult.slice(66, 130), 16),
|
|
484
|
-
summaryValueDecimals: parseInt(repResult.slice(130, 194), 16),
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
} catch {
|
|
489
|
-
// Reputation might not exist yet
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Parse name/description from agentURI
|
|
493
|
-
let name = `Agent #${agentId}`;
|
|
494
|
-
let description = '';
|
|
495
|
-
|
|
496
|
-
if (agentURI.startsWith('data:application/json')) {
|
|
497
|
-
try {
|
|
498
|
-
let jsonStr: string;
|
|
499
|
-
if (agentURI.includes(';base64,')) {
|
|
500
|
-
const base64 = agentURI.split(';base64,')[1];
|
|
501
|
-
jsonStr = atob(base64);
|
|
502
|
-
} else {
|
|
503
|
-
jsonStr = decodeURIComponent(agentURI.split(',')[1]);
|
|
504
|
-
}
|
|
505
|
-
const meta = JSON.parse(jsonStr);
|
|
506
|
-
name = meta.name || name;
|
|
507
|
-
description = meta.description || '';
|
|
508
|
-
} catch {
|
|
509
|
-
// Couldn't parse, use defaults
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return {
|
|
514
|
-
id: '0x' + agentId.toString(16),
|
|
515
|
-
agentIdBigInt: agentId.toString(),
|
|
516
|
-
owner,
|
|
517
|
-
agentURI,
|
|
518
|
-
agentWallet,
|
|
519
|
-
name,
|
|
520
|
-
description,
|
|
521
|
-
skills,
|
|
522
|
-
endpoint,
|
|
523
|
-
priceWei,
|
|
524
|
-
flaunchToken,
|
|
525
|
-
reputation,
|
|
526
|
-
};
|
|
527
|
-
} catch (err) {
|
|
528
|
-
console.error(`Error fetching agent ${agentId}:`, err);
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// ETH price cache for USD conversion
|
|
534
|
-
let ethPriceCache: { price: number; fetchedAt: number } | null = null;
|
|
535
|
-
const ETH_PRICE_CACHE_TTL = 60_000; // 1 minute
|
|
536
|
-
|
|
537
|
-
async function getEthPrice(): Promise<number> {
|
|
538
|
-
const now = Date.now();
|
|
539
|
-
if (ethPriceCache && now - ethPriceCache.fetchedAt < ETH_PRICE_CACHE_TTL) {
|
|
540
|
-
return ethPriceCache.price;
|
|
541
|
-
}
|
|
542
|
-
try {
|
|
543
|
-
const res = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd');
|
|
544
|
-
if (!res.ok) return ethPriceCache?.price ?? 2500;
|
|
545
|
-
const data = await res.json() as { ethereum: { usd: number } };
|
|
546
|
-
const price = data.ethereum.usd;
|
|
547
|
-
ethPriceCache = { price, fetchedAt: now };
|
|
548
|
-
return price;
|
|
549
|
-
} catch {
|
|
550
|
-
return ethPriceCache?.price ?? 2500;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// Fetch market data from Flaunch + DexScreener
|
|
555
|
-
async function getFlaunchData(tokenAddress: string): Promise<{
|
|
556
|
-
marketCapUSD: number;
|
|
557
|
-
volume24hUSD: number;
|
|
558
|
-
priceChange24h: number;
|
|
559
|
-
holders: number;
|
|
560
|
-
image: string;
|
|
561
|
-
symbol: string;
|
|
562
|
-
liquidityUSD: number;
|
|
563
|
-
} | null> {
|
|
564
|
-
try {
|
|
565
|
-
// Fetch from Flaunch (image, symbol, holders), DexScreener (volume, priceChange), and ETH price in parallel
|
|
566
|
-
const [detailsRes, holdersRes, dexRes, ethPrice] = await Promise.all([
|
|
567
|
-
fetch(`${FLAUNCH_API}/tokens/${tokenAddress}`),
|
|
568
|
-
fetch(`${FLAUNCH_API}/tokens/${tokenAddress}/holders?limit=1`),
|
|
569
|
-
fetch(`https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`),
|
|
570
|
-
getEthPrice(),
|
|
571
|
-
]);
|
|
572
|
-
|
|
573
|
-
if (!detailsRes.ok) return null;
|
|
574
|
-
|
|
575
|
-
const details = await detailsRes.json() as {
|
|
576
|
-
tokenAddress: string;
|
|
577
|
-
symbol: string;
|
|
578
|
-
name: string;
|
|
579
|
-
image: string;
|
|
580
|
-
marketCapETH?: string;
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
let holders = 0;
|
|
584
|
-
if (holdersRes.ok) {
|
|
585
|
-
const holdersData = await holdersRes.json() as { totalHolders?: string };
|
|
586
|
-
holders = parseInt(holdersData.totalHolders || '0', 10);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// Get volume and price change from DexScreener
|
|
590
|
-
let volume24hUSD = 0;
|
|
591
|
-
let priceChange24h = 0;
|
|
592
|
-
let liquidityUSD = 0;
|
|
593
|
-
if (dexRes.ok) {
|
|
594
|
-
const dexData = await dexRes.json() as {
|
|
595
|
-
pairs?: Array<{
|
|
596
|
-
priceChange?: { h24?: number };
|
|
597
|
-
volume?: { h24?: number };
|
|
598
|
-
liquidity?: { usd?: number };
|
|
599
|
-
}>;
|
|
600
|
-
};
|
|
601
|
-
const pair = dexData.pairs?.[0];
|
|
602
|
-
if (pair) {
|
|
603
|
-
volume24hUSD = pair.volume?.h24 || 0;
|
|
604
|
-
priceChange24h = pair.priceChange?.h24 || 0;
|
|
605
|
-
liquidityUSD = pair.liquidity?.usd || 0;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// Convert marketCap from ETH to USD
|
|
610
|
-
const marketCapETH = Number(details.marketCapETH || '0') / 1e18;
|
|
611
|
-
const marketCapUSD = marketCapETH * ethPrice;
|
|
612
|
-
|
|
613
|
-
return {
|
|
614
|
-
marketCapUSD,
|
|
615
|
-
volume24hUSD,
|
|
616
|
-
priceChange24h,
|
|
617
|
-
holders,
|
|
618
|
-
image: details.image || '',
|
|
619
|
-
symbol: details.symbol || '',
|
|
620
|
-
liquidityUSD,
|
|
621
|
-
};
|
|
622
|
-
} catch {
|
|
623
|
-
return null;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Cache for agents (5 minute TTL)
|
|
628
|
-
let agentsCache: { agents: Agent[]; fetchedAt: number } | null = null;
|
|
629
|
-
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
630
|
-
|
|
631
|
-
/**
|
|
632
|
-
* Get all Moltlaunch agents (from KV index)
|
|
633
|
-
*/
|
|
634
|
-
export async function getAllAgents(env: Env): Promise<Agent[]> {
|
|
635
|
-
const now = Date.now();
|
|
636
|
-
if (agentsCache && now - agentsCache.fetchedAt < CACHE_TTL) {
|
|
637
|
-
return agentsCache.agents;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
const rpcUrl = env.ALCHEMY_RPC || env.BASE_RPC || 'https://mainnet.base.org';
|
|
641
|
-
|
|
642
|
-
try {
|
|
643
|
-
// Get agent IDs from our KV index
|
|
644
|
-
const indexKey = 'moltlaunch:agents';
|
|
645
|
-
const indexRaw = await env.TASKS_KV.get(indexKey);
|
|
646
|
-
const agentIds: string[] = indexRaw ? JSON.parse(indexRaw) : [];
|
|
647
|
-
|
|
648
|
-
console.log(`[getAllAgents] Found ${agentIds.length} agents in KV index`);
|
|
649
|
-
|
|
650
|
-
if (agentIds.length === 0) {
|
|
651
|
-
return [];
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Fetch details for each agent
|
|
655
|
-
const agents: Agent[] = [];
|
|
656
|
-
|
|
657
|
-
for (const agentId of agentIds) {
|
|
658
|
-
const agent = await getAgentFromContract(rpcUrl, BigInt(agentId));
|
|
659
|
-
if (agent) {
|
|
660
|
-
// Fetch Flaunch market data and burn stats if available
|
|
661
|
-
if (agent.flaunchToken) {
|
|
662
|
-
const [flaunchData, totalBurnedETH, totalBurnedTokens, ethPrice] = await Promise.all([
|
|
663
|
-
getFlaunchData(agent.flaunchToken),
|
|
664
|
-
getTotalBurned(rpcUrl, agent.flaunchToken),
|
|
665
|
-
getTotalTokensBurned(rpcUrl, agent.flaunchToken),
|
|
666
|
-
getEthPrice(),
|
|
667
|
-
]);
|
|
668
|
-
if (flaunchData) {
|
|
669
|
-
agent.marketCapUSD = flaunchData.marketCapUSD;
|
|
670
|
-
agent.volume24hUSD = flaunchData.volume24hUSD;
|
|
671
|
-
agent.priceChange24h = flaunchData.priceChange24h;
|
|
672
|
-
agent.liquidityUSD = flaunchData.liquidityUSD;
|
|
673
|
-
agent.holders = flaunchData.holders;
|
|
674
|
-
agent.image = flaunchData.image;
|
|
675
|
-
agent.symbol = flaunchData.symbol;
|
|
676
|
-
agent.flaunchUrl = `https://flaunch.gg/base/coin/${agent.flaunchToken}`;
|
|
677
|
-
}
|
|
678
|
-
if (totalBurnedETH > 0) {
|
|
679
|
-
agent.totalBurnedETH = totalBurnedETH;
|
|
680
|
-
agent.totalBurnedUSD = totalBurnedETH * ethPrice;
|
|
681
|
-
}
|
|
682
|
-
if (totalBurnedTokens > 0) {
|
|
683
|
-
agent.totalBurnedTokens = totalBurnedTokens;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
agents.push(agent);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// Sort by market cap (if available) or agent ID
|
|
691
|
-
agents.sort((a, b) => {
|
|
692
|
-
if (a.marketCapUSD && b.marketCapUSD) {
|
|
693
|
-
return b.marketCapUSD - a.marketCapUSD;
|
|
694
|
-
}
|
|
695
|
-
if (a.marketCapUSD) return -1;
|
|
696
|
-
if (b.marketCapUSD) return 1;
|
|
697
|
-
return parseInt(b.agentIdBigInt) - parseInt(a.agentIdBigInt);
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
agentsCache = { agents, fetchedAt: now };
|
|
701
|
-
return agents;
|
|
702
|
-
} catch (err) {
|
|
703
|
-
console.error('Error fetching agents:', err);
|
|
704
|
-
return agentsCache?.agents ?? [];
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* Get a single agent by ID
|
|
710
|
-
*/
|
|
711
|
-
export async function getAgentById(env: Env, agentId: string): Promise<Agent | null> {
|
|
712
|
-
const rpcUrl = env.ALCHEMY_RPC || env.BASE_RPC || 'https://mainnet.base.org';
|
|
713
|
-
console.log('[getAgentById] Using RPC:', rpcUrl.substring(0, 50) + '...');
|
|
714
|
-
console.log('[getAgentById] Looking for agent:', agentId);
|
|
715
|
-
|
|
716
|
-
try {
|
|
717
|
-
// Parse agentId (can be hex or decimal)
|
|
718
|
-
const id = agentId.startsWith('0x') ? BigInt(agentId) : BigInt(agentId);
|
|
719
|
-
console.log('[getAgentById] Parsed ID:', id.toString());
|
|
720
|
-
|
|
721
|
-
const agent = await getAgentFromContract(rpcUrl, id);
|
|
722
|
-
console.log('[getAgentById] Got agent:', agent ? 'yes' : 'null');
|
|
723
|
-
|
|
724
|
-
if (agent && agent.flaunchToken) {
|
|
725
|
-
const [flaunchData, totalBurnedETH, totalBurnedTokens, ethPrice] = await Promise.all([
|
|
726
|
-
getFlaunchData(agent.flaunchToken),
|
|
727
|
-
getTotalBurned(rpcUrl, agent.flaunchToken),
|
|
728
|
-
getTotalTokensBurned(rpcUrl, agent.flaunchToken),
|
|
729
|
-
getEthPrice(),
|
|
730
|
-
]);
|
|
731
|
-
if (flaunchData) {
|
|
732
|
-
agent.marketCapUSD = flaunchData.marketCapUSD;
|
|
733
|
-
agent.volume24hUSD = flaunchData.volume24hUSD;
|
|
734
|
-
agent.priceChange24h = flaunchData.priceChange24h;
|
|
735
|
-
agent.liquidityUSD = flaunchData.liquidityUSD;
|
|
736
|
-
agent.holders = flaunchData.holders;
|
|
737
|
-
agent.image = flaunchData.image;
|
|
738
|
-
agent.symbol = flaunchData.symbol;
|
|
739
|
-
agent.flaunchUrl = `https://flaunch.gg/base/coin/${agent.flaunchToken}`;
|
|
740
|
-
}
|
|
741
|
-
if (totalBurnedETH > 0) {
|
|
742
|
-
agent.totalBurnedETH = totalBurnedETH;
|
|
743
|
-
agent.totalBurnedUSD = totalBurnedETH * ethPrice;
|
|
744
|
-
}
|
|
745
|
-
if (totalBurnedTokens > 0) {
|
|
746
|
-
agent.totalBurnedTokens = totalBurnedTokens;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
return agent;
|
|
751
|
-
} catch (err) {
|
|
752
|
-
console.error(`Error fetching agent ${agentId}:`, err);
|
|
753
|
-
return null;
|
|
754
|
-
}
|
|
755
|
-
}
|