hybrid 1.2.2 → 1.2.3
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/dist/{agent-BxbcW5UC.d.cts → agent-6fncjbIG.d.ts} +4 -71
- package/dist/{agent-BxbcW5UC.d.ts → agent-MbcG6CoX.d.cts} +4 -71
- package/dist/index.cjs +654 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +654 -1
- package/dist/index.js.map +1 -1
- package/dist/ponder/index.d.cts +2 -1
- package/dist/ponder/index.d.ts +2 -1
- package/dist/tool-CoVdD8Fb.d.cts +73 -0
- package/dist/tool-CoVdD8Fb.d.ts +73 -0
- package/dist/tools/index.cjs +706 -0
- package/dist/tools/index.cjs.map +1 -0
- package/dist/tools/index.d.cts +1860 -0
- package/dist/tools/index.d.ts +1860 -0
- package/dist/tools/index.js +681 -0
- package/dist/tools/index.js.map +1 -0
- package/package.json +10 -5
- package/src/index.ts +3 -0
- package/src/tools/blockchain.ts +607 -0
- package/src/tools/index.ts +50 -0
- package/src/tools/xmtp.ts +392 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Blockchain Tools for Crypto Agents
|
|
3
|
+
*
|
|
4
|
+
* This module provides comprehensive blockchain interaction tools for crypto-enabled agents.
|
|
5
|
+
* Supports Ethereum and other EVM-compatible chains with features like balance checking,
|
|
6
|
+
* transaction sending, contract interaction, and more.
|
|
7
|
+
*
|
|
8
|
+
* @module BlockchainTools
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
createPublicClient,
|
|
13
|
+
createWalletClient,
|
|
14
|
+
formatEther,
|
|
15
|
+
http,
|
|
16
|
+
parseEther,
|
|
17
|
+
type Address,
|
|
18
|
+
type Hash
|
|
19
|
+
} from "viem"
|
|
20
|
+
import { privateKeyToAccount } from "viem/accounts"
|
|
21
|
+
import {
|
|
22
|
+
arbitrum,
|
|
23
|
+
base,
|
|
24
|
+
mainnet,
|
|
25
|
+
optimism,
|
|
26
|
+
polygon,
|
|
27
|
+
sepolia
|
|
28
|
+
} from "viem/chains"
|
|
29
|
+
import { z } from "zod"
|
|
30
|
+
import { createTool } from "../core/tool"
|
|
31
|
+
|
|
32
|
+
// Supported chains configuration
|
|
33
|
+
const SUPPORTED_CHAINS = {
|
|
34
|
+
mainnet,
|
|
35
|
+
sepolia,
|
|
36
|
+
polygon,
|
|
37
|
+
arbitrum,
|
|
38
|
+
optimism,
|
|
39
|
+
base
|
|
40
|
+
} as const
|
|
41
|
+
|
|
42
|
+
type SupportedChain = keyof typeof SUPPORTED_CHAINS
|
|
43
|
+
|
|
44
|
+
// Runtime extension interface for blockchain tools
|
|
45
|
+
export interface BlockchainRuntimeExtension {
|
|
46
|
+
rpcUrl?: string
|
|
47
|
+
privateKey?: string
|
|
48
|
+
defaultChain?: SupportedChain
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get Balance Tool
|
|
53
|
+
*
|
|
54
|
+
* Retrieves the native token balance for a given address on a specified chain.
|
|
55
|
+
*
|
|
56
|
+
* @tool getBalance
|
|
57
|
+
* @category Blockchain
|
|
58
|
+
*
|
|
59
|
+
* @param {string} address - The wallet address to check balance for
|
|
60
|
+
* @param {string} [chain] - The blockchain network (defaults to mainnet)
|
|
61
|
+
*
|
|
62
|
+
* @returns {Promise<{success: boolean, balance: string, balanceWei: string, address: string, chain: string, error?: string}>}
|
|
63
|
+
*/
|
|
64
|
+
export const getBalanceTool = createTool({
|
|
65
|
+
id: "getBalance",
|
|
66
|
+
description:
|
|
67
|
+
"Get the native token balance for a wallet address on a blockchain",
|
|
68
|
+
inputSchema: z.object({
|
|
69
|
+
address: z.string().describe("The wallet address to check balance for"),
|
|
70
|
+
chain: z
|
|
71
|
+
.enum(["mainnet", "sepolia", "polygon", "arbitrum", "optimism", "base"])
|
|
72
|
+
.default("mainnet")
|
|
73
|
+
.describe("The blockchain network to check on")
|
|
74
|
+
}),
|
|
75
|
+
outputSchema: z.object({
|
|
76
|
+
success: z.boolean(),
|
|
77
|
+
balance: z
|
|
78
|
+
.string()
|
|
79
|
+
.describe("Balance in human readable format (ETH, MATIC, etc.)"),
|
|
80
|
+
balanceWei: z.string().describe("Balance in wei (smallest unit)"),
|
|
81
|
+
address: z.string(),
|
|
82
|
+
chain: z.string(),
|
|
83
|
+
error: z.string().optional()
|
|
84
|
+
}),
|
|
85
|
+
execute: async ({ input, runtime }) => {
|
|
86
|
+
try {
|
|
87
|
+
const { address, chain } = input
|
|
88
|
+
const chainConfig = SUPPORTED_CHAINS[chain]
|
|
89
|
+
|
|
90
|
+
// Use runtime RPC URL if provided, otherwise use default
|
|
91
|
+
const rpcUrl =
|
|
92
|
+
(runtime as any).rpcUrl || chainConfig.rpcUrls.default.http[0]
|
|
93
|
+
|
|
94
|
+
const client = createPublicClient({
|
|
95
|
+
chain: chainConfig,
|
|
96
|
+
transport: http(rpcUrl)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
console.log(`🔍 [getBalance] Checking balance for ${address} on ${chain}`)
|
|
100
|
+
|
|
101
|
+
const balanceWei = await client.getBalance({
|
|
102
|
+
address: address as Address
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const balance = formatEther(balanceWei)
|
|
106
|
+
|
|
107
|
+
console.log(
|
|
108
|
+
`✅ [getBalance] Balance: ${balance} ${chainConfig.nativeCurrency.symbol}`
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
balance: `${balance} ${chainConfig.nativeCurrency.symbol}`,
|
|
114
|
+
balanceWei: balanceWei.toString(),
|
|
115
|
+
address,
|
|
116
|
+
chain
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
const errorMessage =
|
|
120
|
+
error instanceof Error ? error.message : String(error)
|
|
121
|
+
console.error("❌ [getBalance] Error:", errorMessage)
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
balance: "0",
|
|
125
|
+
balanceWei: "0",
|
|
126
|
+
address: input.address,
|
|
127
|
+
chain: input.chain,
|
|
128
|
+
error: errorMessage
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get Transaction Tool
|
|
136
|
+
*
|
|
137
|
+
* Retrieves transaction details by transaction hash.
|
|
138
|
+
*
|
|
139
|
+
* @tool getTransaction
|
|
140
|
+
* @category Blockchain
|
|
141
|
+
*
|
|
142
|
+
* @param {string} hash - The transaction hash to look up
|
|
143
|
+
* @param {string} [chain] - The blockchain network (defaults to mainnet)
|
|
144
|
+
*
|
|
145
|
+
* @returns {Promise<{success: boolean, transaction?: object, error?: string}>}
|
|
146
|
+
*/
|
|
147
|
+
export const getTransactionTool = createTool({
|
|
148
|
+
id: "getTransaction",
|
|
149
|
+
description: "Get transaction details by transaction hash",
|
|
150
|
+
inputSchema: z.object({
|
|
151
|
+
hash: z.string().describe("The transaction hash to look up"),
|
|
152
|
+
chain: z
|
|
153
|
+
.enum(["mainnet", "sepolia", "polygon", "arbitrum", "optimism", "base"])
|
|
154
|
+
.default("mainnet")
|
|
155
|
+
.describe("The blockchain network to check on")
|
|
156
|
+
}),
|
|
157
|
+
outputSchema: z.object({
|
|
158
|
+
success: z.boolean(),
|
|
159
|
+
transaction: z
|
|
160
|
+
.object({
|
|
161
|
+
hash: z.string(),
|
|
162
|
+
from: z.string(),
|
|
163
|
+
to: z.string().nullable(),
|
|
164
|
+
value: z.string(),
|
|
165
|
+
gasUsed: z.string().optional(),
|
|
166
|
+
gasPrice: z.string().optional(),
|
|
167
|
+
blockNumber: z.string().optional(),
|
|
168
|
+
status: z.string().optional()
|
|
169
|
+
})
|
|
170
|
+
.optional(),
|
|
171
|
+
error: z.string().optional()
|
|
172
|
+
}),
|
|
173
|
+
execute: async ({ input, runtime }) => {
|
|
174
|
+
try {
|
|
175
|
+
const { hash, chain } = input
|
|
176
|
+
const chainConfig = SUPPORTED_CHAINS[chain]
|
|
177
|
+
|
|
178
|
+
const rpcUrl =
|
|
179
|
+
(runtime as any).rpcUrl || chainConfig.rpcUrls.default.http[0]
|
|
180
|
+
|
|
181
|
+
const client = createPublicClient({
|
|
182
|
+
chain: chainConfig,
|
|
183
|
+
transport: http(rpcUrl)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
console.log(
|
|
187
|
+
`🔍 [getTransaction] Looking up transaction ${hash} on ${chain}`
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
const transaction = await client.getTransaction({
|
|
191
|
+
hash: hash as Hash
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
const receipt = await client
|
|
195
|
+
.getTransactionReceipt({
|
|
196
|
+
hash: hash as Hash
|
|
197
|
+
})
|
|
198
|
+
.catch(() => null) // Transaction might be pending
|
|
199
|
+
|
|
200
|
+
console.log(
|
|
201
|
+
`✅ [getTransaction] Found transaction from ${transaction.from} to ${transaction.to}`
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
success: true,
|
|
206
|
+
transaction: {
|
|
207
|
+
hash: transaction.hash,
|
|
208
|
+
from: transaction.from,
|
|
209
|
+
to: transaction.to,
|
|
210
|
+
value: formatEther(transaction.value),
|
|
211
|
+
gasUsed: receipt?.gasUsed.toString(),
|
|
212
|
+
gasPrice: transaction.gasPrice?.toString(),
|
|
213
|
+
blockNumber: transaction.blockNumber?.toString(),
|
|
214
|
+
status:
|
|
215
|
+
receipt?.status === "success"
|
|
216
|
+
? "success"
|
|
217
|
+
: receipt?.status === "reverted"
|
|
218
|
+
? "failed"
|
|
219
|
+
: "pending"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
const errorMessage =
|
|
224
|
+
error instanceof Error ? error.message : String(error)
|
|
225
|
+
console.error("❌ [getTransaction] Error:", errorMessage)
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: errorMessage
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Send Transaction Tool
|
|
236
|
+
*
|
|
237
|
+
* Sends a native token transaction to another address.
|
|
238
|
+
* Requires a private key to be configured in the runtime.
|
|
239
|
+
*
|
|
240
|
+
* @tool sendTransaction
|
|
241
|
+
* @category Blockchain
|
|
242
|
+
*
|
|
243
|
+
* @param {string} to - The recipient address
|
|
244
|
+
* @param {string} amount - The amount to send (in ETH, MATIC, etc.)
|
|
245
|
+
* @param {string} [chain] - The blockchain network (defaults to mainnet)
|
|
246
|
+
*
|
|
247
|
+
* @returns {Promise<{success: boolean, hash?: string, error?: string}>}
|
|
248
|
+
*/
|
|
249
|
+
export const sendTransactionTool = createTool({
|
|
250
|
+
id: "sendTransaction",
|
|
251
|
+
description: "Send native tokens to another address",
|
|
252
|
+
inputSchema: z.object({
|
|
253
|
+
to: z.string().describe("The recipient address"),
|
|
254
|
+
amount: z.string().describe("The amount to send (in ETH, MATIC, etc.)"),
|
|
255
|
+
chain: z
|
|
256
|
+
.enum(["mainnet", "sepolia", "polygon", "arbitrum", "optimism", "base"])
|
|
257
|
+
.default("mainnet")
|
|
258
|
+
.describe("The blockchain network to send on")
|
|
259
|
+
}),
|
|
260
|
+
outputSchema: z.object({
|
|
261
|
+
success: z.boolean(),
|
|
262
|
+
hash: z.string().optional(),
|
|
263
|
+
from: z.string().optional(),
|
|
264
|
+
to: z.string(),
|
|
265
|
+
amount: z.string(),
|
|
266
|
+
chain: z.string(),
|
|
267
|
+
error: z.string().optional()
|
|
268
|
+
}),
|
|
269
|
+
execute: async ({ input, runtime }) => {
|
|
270
|
+
try {
|
|
271
|
+
const { to, amount, chain } = input
|
|
272
|
+
const chainConfig = SUPPORTED_CHAINS[chain]
|
|
273
|
+
|
|
274
|
+
const privateKey = (runtime as any).privateKey
|
|
275
|
+
if (!privateKey) {
|
|
276
|
+
return {
|
|
277
|
+
success: false,
|
|
278
|
+
to,
|
|
279
|
+
amount,
|
|
280
|
+
chain,
|
|
281
|
+
error: "Private key not configured in runtime"
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const rpcUrl =
|
|
286
|
+
(runtime as any).rpcUrl || chainConfig.rpcUrls.default.http[0]
|
|
287
|
+
const account = privateKeyToAccount(privateKey as `0x${string}`)
|
|
288
|
+
|
|
289
|
+
const client = createWalletClient({
|
|
290
|
+
account,
|
|
291
|
+
chain: chainConfig,
|
|
292
|
+
transport: http(rpcUrl)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
console.log(
|
|
296
|
+
`💸 [sendTransaction] Sending ${amount} ${chainConfig.nativeCurrency.symbol} to ${to} on ${chain}`
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
const hash = await client.sendTransaction({
|
|
300
|
+
to: to as Address,
|
|
301
|
+
value: parseEther(amount)
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
console.log(`✅ [sendTransaction] Transaction sent: ${hash}`)
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
success: true,
|
|
308
|
+
hash,
|
|
309
|
+
from: account.address,
|
|
310
|
+
to,
|
|
311
|
+
amount,
|
|
312
|
+
chain
|
|
313
|
+
}
|
|
314
|
+
} catch (error) {
|
|
315
|
+
const errorMessage =
|
|
316
|
+
error instanceof Error ? error.message : String(error)
|
|
317
|
+
console.error("❌ [sendTransaction] Error:", errorMessage)
|
|
318
|
+
return {
|
|
319
|
+
success: false,
|
|
320
|
+
to: input.to,
|
|
321
|
+
amount: input.amount,
|
|
322
|
+
chain: input.chain,
|
|
323
|
+
error: errorMessage
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get Block Tool
|
|
331
|
+
*
|
|
332
|
+
* Retrieves information about a specific block.
|
|
333
|
+
*
|
|
334
|
+
* @tool getBlock
|
|
335
|
+
* @category Blockchain
|
|
336
|
+
*
|
|
337
|
+
* @param {string} [blockNumber] - Block number (defaults to latest)
|
|
338
|
+
* @param {string} [chain] - The blockchain network (defaults to mainnet)
|
|
339
|
+
*
|
|
340
|
+
* @returns {Promise<{success: boolean, block?: object, error?: string}>}
|
|
341
|
+
*/
|
|
342
|
+
export const getBlockTool = createTool({
|
|
343
|
+
id: "getBlock",
|
|
344
|
+
description: "Get information about a blockchain block",
|
|
345
|
+
inputSchema: z.object({
|
|
346
|
+
blockNumber: z
|
|
347
|
+
.string()
|
|
348
|
+
.optional()
|
|
349
|
+
.describe("Block number (defaults to latest)"),
|
|
350
|
+
chain: z
|
|
351
|
+
.enum(["mainnet", "sepolia", "polygon", "arbitrum", "optimism", "base"])
|
|
352
|
+
.default("mainnet")
|
|
353
|
+
.describe("The blockchain network to check on")
|
|
354
|
+
}),
|
|
355
|
+
outputSchema: z.object({
|
|
356
|
+
success: z.boolean(),
|
|
357
|
+
block: z
|
|
358
|
+
.object({
|
|
359
|
+
number: z.string(),
|
|
360
|
+
hash: z.string(),
|
|
361
|
+
timestamp: z.string(),
|
|
362
|
+
transactionCount: z.number(),
|
|
363
|
+
gasUsed: z.string(),
|
|
364
|
+
gasLimit: z.string()
|
|
365
|
+
})
|
|
366
|
+
.optional(),
|
|
367
|
+
error: z.string().optional()
|
|
368
|
+
}),
|
|
369
|
+
execute: async ({ input, runtime }) => {
|
|
370
|
+
try {
|
|
371
|
+
const { blockNumber, chain } = input
|
|
372
|
+
const chainConfig = SUPPORTED_CHAINS[chain]
|
|
373
|
+
|
|
374
|
+
const rpcUrl =
|
|
375
|
+
(runtime as any).rpcUrl || chainConfig.rpcUrls.default.http[0]
|
|
376
|
+
|
|
377
|
+
const client = createPublicClient({
|
|
378
|
+
chain: chainConfig,
|
|
379
|
+
transport: http(rpcUrl)
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
console.log(
|
|
383
|
+
`🔍 [getBlock] Getting block ${blockNumber || "latest"} on ${chain}`
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
const block = await client.getBlock({
|
|
387
|
+
blockNumber: blockNumber ? BigInt(blockNumber) : undefined
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
console.log(
|
|
391
|
+
`✅ [getBlock] Found block ${block.number} with ${block.transactions.length} transactions`
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
success: true,
|
|
396
|
+
block: {
|
|
397
|
+
number: block.number.toString(),
|
|
398
|
+
hash: block.hash,
|
|
399
|
+
timestamp: block.timestamp.toString(),
|
|
400
|
+
transactionCount: block.transactions.length,
|
|
401
|
+
gasUsed: block.gasUsed.toString(),
|
|
402
|
+
gasLimit: block.gasLimit.toString()
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
} catch (error) {
|
|
406
|
+
const errorMessage =
|
|
407
|
+
error instanceof Error ? error.message : String(error)
|
|
408
|
+
console.error("❌ [getBlock] Error:", errorMessage)
|
|
409
|
+
return {
|
|
410
|
+
success: false,
|
|
411
|
+
error: errorMessage
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Get Gas Price Tool
|
|
419
|
+
*
|
|
420
|
+
* Retrieves current gas price information for a blockchain.
|
|
421
|
+
*
|
|
422
|
+
* @tool getGasPrice
|
|
423
|
+
* @category Blockchain
|
|
424
|
+
*
|
|
425
|
+
* @param {string} [chain] - The blockchain network (defaults to mainnet)
|
|
426
|
+
*
|
|
427
|
+
* @returns {Promise<{success: boolean, gasPrice?: string, error?: string}>}
|
|
428
|
+
*/
|
|
429
|
+
export const getGasPriceTool = createTool({
|
|
430
|
+
id: "getGasPrice",
|
|
431
|
+
description: "Get current gas price for a blockchain",
|
|
432
|
+
inputSchema: z.object({
|
|
433
|
+
chain: z
|
|
434
|
+
.enum(["mainnet", "sepolia", "polygon", "arbitrum", "optimism", "base"])
|
|
435
|
+
.default("mainnet")
|
|
436
|
+
.describe("The blockchain network to check on")
|
|
437
|
+
}),
|
|
438
|
+
outputSchema: z.object({
|
|
439
|
+
success: z.boolean(),
|
|
440
|
+
gasPrice: z.string().optional().describe("Gas price in gwei"),
|
|
441
|
+
gasPriceWei: z.string().optional().describe("Gas price in wei"),
|
|
442
|
+
chain: z.string(),
|
|
443
|
+
error: z.string().optional()
|
|
444
|
+
}),
|
|
445
|
+
execute: async ({ input, runtime }) => {
|
|
446
|
+
try {
|
|
447
|
+
const { chain } = input
|
|
448
|
+
const chainConfig = SUPPORTED_CHAINS[chain]
|
|
449
|
+
|
|
450
|
+
const rpcUrl =
|
|
451
|
+
(runtime as any).rpcUrl || chainConfig.rpcUrls.default.http[0]
|
|
452
|
+
|
|
453
|
+
const client = createPublicClient({
|
|
454
|
+
chain: chainConfig,
|
|
455
|
+
transport: http(rpcUrl)
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
console.log(`⛽ [getGasPrice] Getting gas price for ${chain}`)
|
|
459
|
+
|
|
460
|
+
const gasPrice = await client.getGasPrice()
|
|
461
|
+
const gasPriceGwei = formatEther(gasPrice * BigInt(1000000000)) // Convert to gwei
|
|
462
|
+
|
|
463
|
+
console.log(`✅ [getGasPrice] Current gas price: ${gasPriceGwei} gwei`)
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
success: true,
|
|
467
|
+
gasPrice: `${gasPriceGwei} gwei`,
|
|
468
|
+
gasPriceWei: gasPrice.toString(),
|
|
469
|
+
chain
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
const errorMessage =
|
|
473
|
+
error instanceof Error ? error.message : String(error)
|
|
474
|
+
console.error("❌ [getGasPrice] Error:", errorMessage)
|
|
475
|
+
return {
|
|
476
|
+
success: false,
|
|
477
|
+
chain: input.chain,
|
|
478
|
+
error: errorMessage
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Estimate Gas Tool
|
|
486
|
+
*
|
|
487
|
+
* Estimates gas required for a transaction.
|
|
488
|
+
*
|
|
489
|
+
* @tool estimateGas
|
|
490
|
+
* @category Blockchain
|
|
491
|
+
*
|
|
492
|
+
* @param {string} to - The recipient address
|
|
493
|
+
* @param {string} [amount] - The amount to send (defaults to 0)
|
|
494
|
+
* @param {string} [data] - Transaction data (for contract calls)
|
|
495
|
+
* @param {string} [chain] - The blockchain network (defaults to mainnet)
|
|
496
|
+
*
|
|
497
|
+
* @returns {Promise<{success: boolean, gasEstimate?: string, error?: string}>}
|
|
498
|
+
*/
|
|
499
|
+
export const estimateGasTool = createTool({
|
|
500
|
+
id: "estimateGas",
|
|
501
|
+
description: "Estimate gas required for a transaction",
|
|
502
|
+
inputSchema: z.object({
|
|
503
|
+
to: z.string().describe("The recipient address"),
|
|
504
|
+
amount: z
|
|
505
|
+
.string()
|
|
506
|
+
.default("0")
|
|
507
|
+
.describe("The amount to send (defaults to 0)"),
|
|
508
|
+
data: z
|
|
509
|
+
.string()
|
|
510
|
+
.optional()
|
|
511
|
+
.describe("Transaction data (for contract calls)"),
|
|
512
|
+
chain: z
|
|
513
|
+
.enum(["mainnet", "sepolia", "polygon", "arbitrum", "optimism", "base"])
|
|
514
|
+
.default("mainnet")
|
|
515
|
+
.describe("The blockchain network to estimate on")
|
|
516
|
+
}),
|
|
517
|
+
outputSchema: z.object({
|
|
518
|
+
success: z.boolean(),
|
|
519
|
+
gasEstimate: z.string().optional(),
|
|
520
|
+
to: z.string(),
|
|
521
|
+
amount: z.string(),
|
|
522
|
+
chain: z.string(),
|
|
523
|
+
error: z.string().optional()
|
|
524
|
+
}),
|
|
525
|
+
execute: async ({ input, runtime }) => {
|
|
526
|
+
try {
|
|
527
|
+
const { to, amount, data, chain } = input
|
|
528
|
+
const chainConfig = SUPPORTED_CHAINS[chain]
|
|
529
|
+
|
|
530
|
+
const privateKey = (runtime as any).privateKey
|
|
531
|
+
if (!privateKey) {
|
|
532
|
+
return {
|
|
533
|
+
success: false,
|
|
534
|
+
to,
|
|
535
|
+
amount,
|
|
536
|
+
chain,
|
|
537
|
+
error: "Private key not configured in runtime"
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const rpcUrl =
|
|
542
|
+
(runtime as any).rpcUrl || chainConfig.rpcUrls.default.http[0]
|
|
543
|
+
const account = privateKeyToAccount(privateKey as `0x${string}`)
|
|
544
|
+
|
|
545
|
+
const client = createPublicClient({
|
|
546
|
+
chain: chainConfig,
|
|
547
|
+
transport: http(rpcUrl)
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
console.log(
|
|
551
|
+
`⛽ [estimateGas] Estimating gas for transaction to ${to} on ${chain}`
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
const gasEstimate = await client.estimateGas({
|
|
555
|
+
account: account.address,
|
|
556
|
+
to: to as Address,
|
|
557
|
+
value: parseEther(amount),
|
|
558
|
+
data: data as `0x${string}` | undefined
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
console.log(`✅ [estimateGas] Estimated gas: ${gasEstimate.toString()}`)
|
|
562
|
+
|
|
563
|
+
return {
|
|
564
|
+
success: true,
|
|
565
|
+
gasEstimate: gasEstimate.toString(),
|
|
566
|
+
to,
|
|
567
|
+
amount,
|
|
568
|
+
chain
|
|
569
|
+
}
|
|
570
|
+
} catch (error) {
|
|
571
|
+
const errorMessage =
|
|
572
|
+
error instanceof Error ? error.message : String(error)
|
|
573
|
+
console.error("❌ [estimateGas] Error:", errorMessage)
|
|
574
|
+
return {
|
|
575
|
+
success: false,
|
|
576
|
+
to: input.to,
|
|
577
|
+
amount: input.amount,
|
|
578
|
+
chain: input.chain,
|
|
579
|
+
error: errorMessage
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Collection of blockchain tools for crypto agents
|
|
587
|
+
*
|
|
588
|
+
* These tools provide comprehensive blockchain interaction capabilities including
|
|
589
|
+
* balance checking, transaction sending, gas estimation, and more.
|
|
590
|
+
*
|
|
591
|
+
* @namespace blockchainTools
|
|
592
|
+
*
|
|
593
|
+
* @property {Tool} getBalance - Get native token balance for an address
|
|
594
|
+
* @property {Tool} getTransaction - Get transaction details by hash
|
|
595
|
+
* @property {Tool} sendTransaction - Send native tokens to another address
|
|
596
|
+
* @property {Tool} getBlock - Get information about a blockchain block
|
|
597
|
+
* @property {Tool} getGasPrice - Get current gas price for a blockchain
|
|
598
|
+
* @property {Tool} estimateGas - Estimate gas required for a transaction
|
|
599
|
+
*/
|
|
600
|
+
export const blockchainTools = {
|
|
601
|
+
getBalance: getBalanceTool,
|
|
602
|
+
getTransaction: getTransactionTool,
|
|
603
|
+
sendTransaction: sendTransactionTool,
|
|
604
|
+
getBlock: getBlockTool,
|
|
605
|
+
getGasPrice: getGasPriceTool,
|
|
606
|
+
estimateGas: estimateGasTool
|
|
607
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Hybrid Agent Tools Standard Library
|
|
3
|
+
*
|
|
4
|
+
* This module provides a comprehensive set of tools for building crypto-enabled agents.
|
|
5
|
+
* Tools are organized by category and can be imported individually or as complete sets.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { blockchainTools, xmtpTools } from "hybrid/tools"
|
|
10
|
+
* import { Agent } from "hybrid"
|
|
11
|
+
*
|
|
12
|
+
* const agent = new Agent({
|
|
13
|
+
* name: "crypto-agent",
|
|
14
|
+
* model: myModel,
|
|
15
|
+
* tools: {
|
|
16
|
+
* ...blockchainTools,
|
|
17
|
+
* ...xmtpTools
|
|
18
|
+
* },
|
|
19
|
+
* instructions: "You are a crypto agent with blockchain and messaging capabilities.",
|
|
20
|
+
* createRuntime: (runtime) => ({
|
|
21
|
+
* rpcUrl: process.env.RPC_URL,
|
|
22
|
+
* privateKey: process.env.PRIVATE_KEY,
|
|
23
|
+
* defaultChain: "mainnet" as const
|
|
24
|
+
* })
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @module HybridTools
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// Export blockchain tools
|
|
32
|
+
export {
|
|
33
|
+
blockchainTools,
|
|
34
|
+
estimateGasTool,
|
|
35
|
+
getBalanceTool,
|
|
36
|
+
getBlockTool,
|
|
37
|
+
getGasPriceTool,
|
|
38
|
+
getTransactionTool,
|
|
39
|
+
sendTransactionTool,
|
|
40
|
+
type BlockchainRuntimeExtension
|
|
41
|
+
} from "./blockchain"
|
|
42
|
+
|
|
43
|
+
// Export XMTP tools
|
|
44
|
+
export {
|
|
45
|
+
getMessageTool,
|
|
46
|
+
sendMessageTool,
|
|
47
|
+
sendReactionTool,
|
|
48
|
+
sendReplyTool,
|
|
49
|
+
xmtpTools
|
|
50
|
+
} from "./xmtp"
|