web3agent 0.1.0 → 0.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.
@@ -0,0 +1,303 @@
1
+ import {
2
+ RESTRICTED_PLUGIN_CHAINS,
3
+ Web3AgentError,
4
+ assertAddress,
5
+ assertChainSupported,
6
+ buildGoatTools,
7
+ createPublicClientForRuntimeChain,
8
+ findRestrictedPlugin,
9
+ getChainForRuntime,
10
+ getConfirmedReceipt,
11
+ getRpcUrlForRuntimeChain,
12
+ getRuntimeConfigForChain,
13
+ loadPlugins,
14
+ lookupTokenByAddress
15
+ } from "./chunk-BJUU5ESI.js";
16
+ import "./chunk-KPRIUALX.js";
17
+ import "./chunk-7QEKU2RP.js";
18
+
19
+ // src/operations/goat-wallet.ts
20
+ import { encodeFunctionData, parseAbi } from "viem";
21
+ var erc20BalanceAbi = parseAbi(["function balanceOf(address account) view returns (uint256)"]);
22
+ function normalizeTypedDataTypes(types) {
23
+ const normalized = {};
24
+ for (const [typeName, entries] of Object.entries(types)) {
25
+ if (!Array.isArray(entries)) {
26
+ throw new Web3AgentError({
27
+ code: "GOAT_TOOL_ERROR",
28
+ message: `Typed data type ${typeName} must be an array`
29
+ });
30
+ }
31
+ normalized[typeName] = entries.map((entry, index) => {
32
+ if (!entry || typeof entry !== "object") {
33
+ throw new Web3AgentError({
34
+ code: "GOAT_TOOL_ERROR",
35
+ message: `Typed data entry ${typeName}[${index}] must be an object`
36
+ });
37
+ }
38
+ const name = entry.name;
39
+ const type = entry.type;
40
+ if (typeof name !== "string" || typeof type !== "string") {
41
+ throw new Web3AgentError({
42
+ code: "GOAT_TOOL_ERROR",
43
+ message: `Typed data entry ${typeName}[${index}] must include string name/type`
44
+ });
45
+ }
46
+ return { name, type };
47
+ });
48
+ }
49
+ return normalized;
50
+ }
51
+ var OperationPauseError = class extends Error {
52
+ constructor(action) {
53
+ super(`Operation paused for ${action.type}`);
54
+ this.action = action;
55
+ this.name = "OperationPauseError";
56
+ }
57
+ };
58
+ var PreparedActionGoatWallet = class {
59
+ constructor(options) {
60
+ this.options = options;
61
+ const chain = getChainForRuntime(this.options.chainId);
62
+ this.publicClient = createPublicClientForRuntimeChain(this.options.chainId);
63
+ this.chain = {
64
+ type: "evm",
65
+ id: chain.id,
66
+ nativeCurrency: chain.nativeCurrency
67
+ };
68
+ }
69
+ publicClient;
70
+ chain;
71
+ transactionIndex = 0;
72
+ signTypedDataIndex = 0;
73
+ signMessageIndex = 0;
74
+ getAddress() {
75
+ return assertAddress(this.options.account, "account");
76
+ }
77
+ getChain() {
78
+ return this.chain;
79
+ }
80
+ getCoreTools() {
81
+ return [];
82
+ }
83
+ async signMessage(message) {
84
+ const id = `sign-message:${this.signMessageIndex++}`;
85
+ const result = this.options.actionResults[id];
86
+ if (result?.type === "signature" || result?.type === "messageSignature") {
87
+ return { signature: result.signature };
88
+ }
89
+ throw new OperationPauseError({
90
+ id,
91
+ type: "signMessage",
92
+ label: "Sign message",
93
+ chainId: this.options.chainId,
94
+ message
95
+ });
96
+ }
97
+ async signTypedData(data) {
98
+ const id = `sign-typed-data:${this.signTypedDataIndex++}`;
99
+ const result = this.options.actionResults[id];
100
+ if (result?.type === "signature") {
101
+ return { signature: result.signature };
102
+ }
103
+ throw new OperationPauseError({
104
+ id,
105
+ type: "signTypedData",
106
+ label: "Sign typed data",
107
+ chainId: this.options.chainId,
108
+ eip712: {
109
+ domain: data.domain,
110
+ types: normalizeTypedDataTypes(data.types),
111
+ primaryType: data.primaryType,
112
+ message: data.message
113
+ }
114
+ });
115
+ }
116
+ async sendTransaction(transaction) {
117
+ const id = `transaction:${this.transactionIndex++}`;
118
+ const result = this.options.actionResults[id];
119
+ if (result?.type === "transaction") {
120
+ const to2 = assertAddress(transaction.to, "transaction.to");
121
+ const action = {
122
+ id,
123
+ type: "transaction",
124
+ label: `Execute transaction to ${to2}`,
125
+ tx: {
126
+ to: to2,
127
+ chainId: this.options.chainId
128
+ }
129
+ };
130
+ await getConfirmedReceipt(action, result);
131
+ return { hash: result.txHash };
132
+ }
133
+ const to = assertAddress(transaction.to, "transaction.to");
134
+ const data = transaction.data ?? (transaction.abi && transaction.functionName ? encodeFunctionData({
135
+ abi: transaction.abi,
136
+ functionName: transaction.functionName,
137
+ args: transaction.args
138
+ }) : void 0);
139
+ throw new OperationPauseError({
140
+ id,
141
+ type: "transaction",
142
+ label: transaction.functionName ? `Execute ${transaction.functionName}` : `Execute transaction to ${to}`,
143
+ tx: {
144
+ to,
145
+ chainId: this.options.chainId,
146
+ ...data ? { data } : {},
147
+ ...transaction.value !== void 0 ? { value: transaction.value.toString() } : {}
148
+ }
149
+ });
150
+ }
151
+ async read(request) {
152
+ const value = await this.publicClient.readContract({
153
+ address: assertAddress(request.address, "read.address"),
154
+ abi: request.abi,
155
+ functionName: request.functionName,
156
+ ...request.args ? { args: request.args } : {}
157
+ });
158
+ return { value };
159
+ }
160
+ async getNativeBalance() {
161
+ return this.publicClient.getBalance({
162
+ address: this.getAddress()
163
+ });
164
+ }
165
+ async balanceOf(address, tokenAddress) {
166
+ if (!tokenAddress) {
167
+ const value = await this.publicClient.getBalance({
168
+ address: assertAddress(address, "address")
169
+ });
170
+ return {
171
+ value: value.toString(),
172
+ decimals: this.chain.nativeCurrency.decimals,
173
+ symbol: this.chain.nativeCurrency.symbol,
174
+ name: this.chain.nativeCurrency.name,
175
+ inBaseUnits: value.toString()
176
+ };
177
+ }
178
+ const normalizedTokenAddress = assertAddress(tokenAddress, "tokenAddress");
179
+ const tokenBalance = await this.publicClient.readContract({
180
+ address: normalizedTokenAddress,
181
+ abi: erc20BalanceAbi,
182
+ functionName: "balanceOf",
183
+ args: [assertAddress(address, "address")]
184
+ });
185
+ const token = lookupTokenByAddress(normalizedTokenAddress, this.options.chainId);
186
+ return {
187
+ value: tokenBalance.toString(),
188
+ decimals: token?.decimals ?? 18,
189
+ symbol: token?.symbol ?? "UNKNOWN",
190
+ name: token?.name ?? "Unknown Token",
191
+ inBaseUnits: tokenBalance.toString()
192
+ };
193
+ }
194
+ async getTokenInfoByTicker(ticker) {
195
+ const toolHint = this.options.toolName ? ` (triggered by ${this.options.toolName})` : "";
196
+ throw new Web3AgentError({
197
+ code: "GOAT_TOOL_ERROR",
198
+ message: `Token lookup by ticker is not supported in prepared GOAT mode (${ticker})${toolHint}; resolve token addresses before running prepared GOAT tools`
199
+ });
200
+ }
201
+ };
202
+
203
+ // src/operations/goat.ts
204
+ function assertGoatToolSupportedOnChain(toolName, chainId) {
205
+ assertChainSupported(chainId);
206
+ const pluginKey = findRestrictedPlugin(toolName);
207
+ if (!pluginKey) return;
208
+ const availableChains = RESTRICTED_PLUGIN_CHAINS[pluginKey];
209
+ if (!availableChains.includes(chainId)) {
210
+ throw new Web3AgentError({
211
+ code: "TOOL_UNAVAILABLE_ON_CHAIN",
212
+ message: `${toolName} is not available on chain ${chainId}. Available on chains: ${availableChains.join(", ")}`,
213
+ details: { availableChainIds: availableChains }
214
+ });
215
+ }
216
+ }
217
+ function toCompletedResult(result) {
218
+ if (result && typeof result === "object" && !Array.isArray(result)) {
219
+ return result;
220
+ }
221
+ return {
222
+ value: result
223
+ };
224
+ }
225
+ function loadGoatPluginsForChain(chainId) {
226
+ const config = getRuntimeConfigForChain(chainId);
227
+ return loadPlugins({
228
+ hasWallet: true,
229
+ zeroxApiKey: config.zeroxApiKey,
230
+ coingeckoApiKey: config.coingeckoApiKey,
231
+ rpcUrl: getRpcUrlForRuntimeChain(chainId, config)
232
+ });
233
+ }
234
+ function createGoatPreparedOperation(params) {
235
+ const { input, tool, actionResults, pause } = params;
236
+ return {
237
+ integration: "goat",
238
+ kind: "tool",
239
+ summary: tool.description || `Prepare GOAT tool ${tool.name} on chain ${input.chainId}`,
240
+ actions: [pause.action],
241
+ resumeState: {
242
+ version: 1,
243
+ integration: "goat",
244
+ kind: "tool",
245
+ state: {
246
+ toolName: input.toolName,
247
+ params: input.params ?? {},
248
+ chainId: input.chainId,
249
+ account: input.account,
250
+ actionResults
251
+ }
252
+ },
253
+ meta: {
254
+ toolName: tool.name,
255
+ description: tool.description
256
+ }
257
+ };
258
+ }
259
+ async function prepareOrResumeGoatOperation(params) {
260
+ const actionResults = params.actionResults ?? {};
261
+ assertGoatToolSupportedOnChain(params.input.toolName, params.input.chainId);
262
+ const pluginResult = loadGoatPluginsForChain(params.input.chainId);
263
+ const wallet = new PreparedActionGoatWallet({
264
+ account: params.input.account,
265
+ chainId: params.input.chainId,
266
+ actionResults,
267
+ toolName: params.input.toolName
268
+ });
269
+ const tools = await buildGoatTools({
270
+ wallet,
271
+ pluginResult
272
+ });
273
+ const executableTool = tools.find((candidate) => candidate.name === params.input.toolName);
274
+ if (!executableTool) {
275
+ throw new Web3AgentError({
276
+ code: "UNKNOWN_TOOL",
277
+ message: `Unknown GOAT tool: ${params.input.toolName}`
278
+ });
279
+ }
280
+ try {
281
+ const parsed = executableTool.parameters.parse(params.input.params ?? {});
282
+ const result = await executableTool.execute(parsed);
283
+ return {
284
+ completed: true,
285
+ integration: "goat",
286
+ kind: "tool",
287
+ result: toCompletedResult(result)
288
+ };
289
+ } catch (error) {
290
+ if (error instanceof OperationPauseError) {
291
+ return createGoatPreparedOperation({
292
+ input: params.input,
293
+ tool: executableTool,
294
+ actionResults,
295
+ pause: error
296
+ });
297
+ }
298
+ throw Web3AgentError.fromUnknown("GOAT_TOOL_ERROR", error);
299
+ }
300
+ }
301
+ export {
302
+ prepareOrResumeGoatOperation
303
+ };