amped-defi 1.0.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.
Files changed (189) hide show
  1. package/README.md +757 -0
  2. package/dist/__mocks__/@sodax/sdk.d.ts +24 -0
  3. package/dist/__mocks__/@sodax/sdk.d.ts.map +1 -0
  4. package/dist/__mocks__/@sodax/sdk.js +24 -0
  5. package/dist/__mocks__/@sodax/sdk.js.map +1 -0
  6. package/dist/__tests__/setup.d.ts +4 -0
  7. package/dist/__tests__/setup.d.ts.map +1 -0
  8. package/dist/__tests__/setup.js +32 -0
  9. package/dist/__tests__/setup.js.map +1 -0
  10. package/dist/index.d.ts +66 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +281 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/policy/policyEngine.d.ts +119 -0
  15. package/dist/policy/policyEngine.d.ts.map +1 -0
  16. package/dist/policy/policyEngine.js +322 -0
  17. package/dist/policy/policyEngine.js.map +1 -0
  18. package/dist/providers/spokeProviderFactory.d.ts +38 -0
  19. package/dist/providers/spokeProviderFactory.d.ts.map +1 -0
  20. package/dist/providers/spokeProviderFactory.js +212 -0
  21. package/dist/providers/spokeProviderFactory.js.map +1 -0
  22. package/dist/sodax/client.d.ts +34 -0
  23. package/dist/sodax/client.d.ts.map +1 -0
  24. package/dist/sodax/client.js +99 -0
  25. package/dist/sodax/client.js.map +1 -0
  26. package/dist/tools/bridge.d.ts +105 -0
  27. package/dist/tools/bridge.d.ts.map +1 -0
  28. package/dist/tools/bridge.js +334 -0
  29. package/dist/tools/bridge.js.map +1 -0
  30. package/dist/tools/discovery.d.ts +141 -0
  31. package/dist/tools/discovery.d.ts.map +1 -0
  32. package/dist/tools/discovery.js +777 -0
  33. package/dist/tools/discovery.js.map +1 -0
  34. package/dist/tools/moneyMarket.d.ts +227 -0
  35. package/dist/tools/moneyMarket.d.ts.map +1 -0
  36. package/dist/tools/moneyMarket.js +867 -0
  37. package/dist/tools/moneyMarket.js.map +1 -0
  38. package/dist/tools/portfolio.d.ts +43 -0
  39. package/dist/tools/portfolio.d.ts.map +1 -0
  40. package/dist/tools/portfolio.js +538 -0
  41. package/dist/tools/portfolio.js.map +1 -0
  42. package/dist/tools/swap.d.ts +71 -0
  43. package/dist/tools/swap.d.ts.map +1 -0
  44. package/dist/tools/swap.js +762 -0
  45. package/dist/tools/swap.js.map +1 -0
  46. package/dist/tools/walletManagement.d.ts +80 -0
  47. package/dist/tools/walletManagement.d.ts.map +1 -0
  48. package/dist/tools/walletManagement.js +289 -0
  49. package/dist/tools/walletManagement.js.map +1 -0
  50. package/dist/types.d.ts +205 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +5 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/utils/errorUtils.d.ts +2 -0
  55. package/dist/utils/errorUtils.d.ts.map +1 -0
  56. package/dist/utils/errorUtils.js +19 -0
  57. package/dist/utils/errorUtils.js.map +1 -0
  58. package/dist/utils/errors.d.ts +144 -0
  59. package/dist/utils/errors.d.ts.map +1 -0
  60. package/dist/utils/errors.js +310 -0
  61. package/dist/utils/errors.js.map +1 -0
  62. package/dist/utils/positionAggregator.d.ts +122 -0
  63. package/dist/utils/positionAggregator.d.ts.map +1 -0
  64. package/dist/utils/positionAggregator.js +377 -0
  65. package/dist/utils/positionAggregator.js.map +1 -0
  66. package/dist/utils/priceService.d.ts +45 -0
  67. package/dist/utils/priceService.d.ts.map +1 -0
  68. package/dist/utils/priceService.js +108 -0
  69. package/dist/utils/priceService.js.map +1 -0
  70. package/dist/utils/sodaxApi.d.ts +92 -0
  71. package/dist/utils/sodaxApi.d.ts.map +1 -0
  72. package/dist/utils/sodaxApi.js +143 -0
  73. package/dist/utils/sodaxApi.js.map +1 -0
  74. package/dist/utils/tokenResolver.d.ts +54 -0
  75. package/dist/utils/tokenResolver.d.ts.map +1 -0
  76. package/dist/utils/tokenResolver.js +252 -0
  77. package/dist/utils/tokenResolver.js.map +1 -0
  78. package/dist/wallet/backendConfig.d.ts +37 -0
  79. package/dist/wallet/backendConfig.d.ts.map +1 -0
  80. package/dist/wallet/backendConfig.js +125 -0
  81. package/dist/wallet/backendConfig.js.map +1 -0
  82. package/dist/wallet/backends/BankrBackend.d.ts +73 -0
  83. package/dist/wallet/backends/BankrBackend.d.ts.map +1 -0
  84. package/dist/wallet/backends/BankrBackend.js +315 -0
  85. package/dist/wallet/backends/BankrBackend.js.map +1 -0
  86. package/dist/wallet/backends/BankrWalletProvider.d.ts +75 -0
  87. package/dist/wallet/backends/BankrWalletProvider.d.ts.map +1 -0
  88. package/dist/wallet/backends/BankrWalletProvider.js +243 -0
  89. package/dist/wallet/backends/BankrWalletProvider.js.map +1 -0
  90. package/dist/wallet/backends/EnvBackend.d.ts +50 -0
  91. package/dist/wallet/backends/EnvBackend.d.ts.map +1 -0
  92. package/dist/wallet/backends/EnvBackend.js +114 -0
  93. package/dist/wallet/backends/EnvBackend.js.map +1 -0
  94. package/dist/wallet/backends/EvmWalletSkillBackend.d.ts +40 -0
  95. package/dist/wallet/backends/EvmWalletSkillBackend.d.ts.map +1 -0
  96. package/dist/wallet/backends/EvmWalletSkillBackend.js +81 -0
  97. package/dist/wallet/backends/EvmWalletSkillBackend.js.map +1 -0
  98. package/dist/wallet/backends/index.d.ts +10 -0
  99. package/dist/wallet/backends/index.d.ts.map +1 -0
  100. package/dist/wallet/backends/index.js +10 -0
  101. package/dist/wallet/backends/index.js.map +1 -0
  102. package/dist/wallet/index.d.ts +9 -0
  103. package/dist/wallet/index.d.ts.map +1 -0
  104. package/dist/wallet/index.js +12 -0
  105. package/dist/wallet/index.js.map +1 -0
  106. package/dist/wallet/providers/AmpedWalletProvider.d.ts +107 -0
  107. package/dist/wallet/providers/AmpedWalletProvider.d.ts.map +1 -0
  108. package/dist/wallet/providers/AmpedWalletProvider.js +208 -0
  109. package/dist/wallet/providers/AmpedWalletProvider.js.map +1 -0
  110. package/dist/wallet/providers/BankrBackend.d.ts +105 -0
  111. package/dist/wallet/providers/BankrBackend.d.ts.map +1 -0
  112. package/dist/wallet/providers/BankrBackend.js +327 -0
  113. package/dist/wallet/providers/BankrBackend.js.map +1 -0
  114. package/dist/wallet/providers/LocalKeyBackend.d.ts +62 -0
  115. package/dist/wallet/providers/LocalKeyBackend.d.ts.map +1 -0
  116. package/dist/wallet/providers/LocalKeyBackend.js +152 -0
  117. package/dist/wallet/providers/LocalKeyBackend.js.map +1 -0
  118. package/dist/wallet/providers/chainConfig.d.ts +209 -0
  119. package/dist/wallet/providers/chainConfig.d.ts.map +1 -0
  120. package/dist/wallet/providers/chainConfig.js +175 -0
  121. package/dist/wallet/providers/chainConfig.js.map +1 -0
  122. package/dist/wallet/providers/index.d.ts +30 -0
  123. package/dist/wallet/providers/index.d.ts.map +1 -0
  124. package/dist/wallet/providers/index.js +32 -0
  125. package/dist/wallet/providers/index.js.map +1 -0
  126. package/dist/wallet/providers/types.d.ts +156 -0
  127. package/dist/wallet/providers/types.d.ts.map +1 -0
  128. package/dist/wallet/providers/types.js +11 -0
  129. package/dist/wallet/providers/types.js.map +1 -0
  130. package/dist/wallet/skillWalletAdapter.d.ts +96 -0
  131. package/dist/wallet/skillWalletAdapter.d.ts.map +1 -0
  132. package/dist/wallet/skillWalletAdapter.js +280 -0
  133. package/dist/wallet/skillWalletAdapter.js.map +1 -0
  134. package/dist/wallet/types.d.ts +134 -0
  135. package/dist/wallet/types.d.ts.map +1 -0
  136. package/dist/wallet/types.js +138 -0
  137. package/dist/wallet/types.js.map +1 -0
  138. package/dist/wallet/walletManager.d.ts +111 -0
  139. package/dist/wallet/walletManager.d.ts.map +1 -0
  140. package/dist/wallet/walletManager.js +476 -0
  141. package/dist/wallet/walletManager.js.map +1 -0
  142. package/dist/wallet/walletRegistry.d.ts +95 -0
  143. package/dist/wallet/walletRegistry.d.ts.map +1 -0
  144. package/dist/wallet/walletRegistry.js +184 -0
  145. package/dist/wallet/walletRegistry.js.map +1 -0
  146. package/index.js +2 -0
  147. package/openclaw.plugin.json +37 -0
  148. package/package.json +69 -0
  149. package/src/__mocks__/@sodax/sdk.ts +28 -0
  150. package/src/__tests__/errors.test.ts +238 -0
  151. package/src/__tests__/policyEngine.test.ts +354 -0
  152. package/src/__tests__/positionAggregator.test.ts +271 -0
  153. package/src/__tests__/setup.ts +35 -0
  154. package/src/__tests__/sodaxApi.test.ts +203 -0
  155. package/src/__tests__/walletRegistry.test.ts +155 -0
  156. package/src/index.ts +376 -0
  157. package/src/policy/policyEngine.ts +389 -0
  158. package/src/providers/spokeProviderFactory.ts +283 -0
  159. package/src/sodax/client.ts +113 -0
  160. package/src/tools/bridge.ts +425 -0
  161. package/src/tools/discovery.ts +989 -0
  162. package/src/tools/moneyMarket.ts +1265 -0
  163. package/src/tools/portfolio.ts +697 -0
  164. package/src/tools/swap.ts +926 -0
  165. package/src/tools/walletManagement.ts +359 -0
  166. package/src/types.ts +228 -0
  167. package/src/utils/errorUtils.ts +16 -0
  168. package/src/utils/errors.ts +396 -0
  169. package/src/utils/positionAggregator.ts +559 -0
  170. package/src/utils/priceService.ts +153 -0
  171. package/src/utils/sodaxApi.ts +261 -0
  172. package/src/utils/tokenResolver.ts +286 -0
  173. package/src/wallet/backendConfig.ts +151 -0
  174. package/src/wallet/backends/BankrBackend.ts +399 -0
  175. package/src/wallet/backends/BankrWalletProvider.ts +329 -0
  176. package/src/wallet/backends/EnvBackend.ts +149 -0
  177. package/src/wallet/backends/EvmWalletSkillBackend.ts +110 -0
  178. package/src/wallet/backends/index.ts +10 -0
  179. package/src/wallet/index.ts +14 -0
  180. package/src/wallet/providers/AmpedWalletProvider.ts +267 -0
  181. package/src/wallet/providers/BankrBackend.ts +407 -0
  182. package/src/wallet/providers/LocalKeyBackend.ts +184 -0
  183. package/src/wallet/providers/chainConfig.ts +194 -0
  184. package/src/wallet/providers/index.ts +62 -0
  185. package/src/wallet/providers/types.ts +186 -0
  186. package/src/wallet/skillWalletAdapter.ts +335 -0
  187. package/src/wallet/types.ts +248 -0
  188. package/src/wallet/walletManager.ts +561 -0
  189. package/src/wallet/walletRegistry.ts +216 -0
@@ -0,0 +1,399 @@
1
+ /**
2
+ * Bankr Backend - Transaction Execution via Bankr API
3
+ *
4
+ * Submits raw transactions to Bankr's Agent API using the
5
+ * arbitrary transaction format documented at:
6
+ * https://github.com/BankrBot/openclaw-skills/blob/main/bankr/references/arbitrary-transaction.md
7
+ *
8
+ * Supported chains: Ethereum (1), Polygon (137), Base (8453)
9
+ */
10
+
11
+ import type { Address, Hash } from 'viem';
12
+ import type { IWalletBackend, RawTransaction } from '../types';
13
+ import { BANKR_SUPPORTED_CHAINS, isBankrSupportedChain } from '../types';
14
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
15
+ import { join, dirname } from 'path';
16
+ import { homedir } from 'os';
17
+
18
+ /**
19
+ * Disk cache path for bankr address
20
+ */
21
+ const BANKR_CACHE_DIR = join(homedir(), '.openclaw', 'cache');
22
+ const getBankrCachePath = (nickname: string) => join(BANKR_CACHE_DIR, `bankr-${nickname}-address.json`);
23
+
24
+ /**
25
+ * Bankr API response types
26
+ */
27
+ interface BankrJobSubmitResponse {
28
+ success: boolean;
29
+ jobId: string;
30
+ status: 'pending';
31
+ message?: string;
32
+ error?: string;
33
+ }
34
+
35
+ interface BankrJobStatusResponse {
36
+ success: boolean;
37
+ jobId: string;
38
+ status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';
39
+ prompt: string;
40
+ response?: string;
41
+ error?: string;
42
+ richData?: Array<{
43
+ type?: string;
44
+ transactionHash?: string;
45
+ txHash?: string;
46
+ hash?: string;
47
+ [key: string]: unknown;
48
+ }>;
49
+ statusUpdates?: Array<{ message: string; timestamp: string }>;
50
+ createdAt: string;
51
+ completedAt?: string;
52
+ }
53
+
54
+ /**
55
+ * Bankr backend configuration
56
+ */
57
+ export interface BankrBackendConfig {
58
+ nickname?: string;
59
+ apiKey: string;
60
+ apiUrl?: string;
61
+ }
62
+
63
+ /**
64
+ * Bankr wallet backend
65
+ * Submits raw transactions via Bankr Agent API
66
+ */
67
+ export class BankrBackend implements IWalletBackend {
68
+ readonly type = 'bankr' as const;
69
+ readonly nickname: string;
70
+ readonly supportedChains = BANKR_SUPPORTED_CHAINS;
71
+
72
+ private readonly apiUrl: string;
73
+ private readonly apiKey: string;
74
+ private cachedAddress: Address | null = null;
75
+ private cachedSolanaAddress: string | null = null;
76
+
77
+ // Polling configuration
78
+ private readonly pollIntervalMs = 2000;
79
+ private readonly maxPollAttempts = 150; // 5 minutes max
80
+
81
+ constructor(config: BankrBackendConfig) {
82
+ this.nickname = config.nickname || 'bankr';
83
+ this.apiUrl = config.apiUrl || 'https://api.bankr.bot';
84
+ this.apiKey = config.apiKey;
85
+
86
+ // Try to load cached address from disk
87
+ this.loadCachedAddress();
88
+
89
+ console.log(`[BankrBackend] Initialized as "${this.nickname}"`);
90
+ console.log(`[BankrBackend] Supported chains: ${this.supportedChains.join(', ')}`);
91
+ if (this.cachedAddress) {
92
+ console.log(`[BankrBackend] Loaded cached address: ${this.cachedAddress}`);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Load cached address from disk
98
+ */
99
+ private loadCachedAddress(): void {
100
+ const cachePath = getBankrCachePath(this.nickname);
101
+ if (existsSync(cachePath)) {
102
+ try {
103
+ const data = JSON.parse(readFileSync(cachePath, 'utf-8'));
104
+ if (data.address && data.address.match(/^0x[a-fA-F0-9]{40}$/)) {
105
+ this.cachedAddress = data.address as Address;
106
+ }
107
+ } catch (e) {
108
+ console.warn(`[BankrBackend] Failed to load cached address: ${e}`);
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Save address to disk cache
115
+ */
116
+ private saveCachedAddress(address: Address): void {
117
+ const cachePath = getBankrCachePath(this.nickname);
118
+ try {
119
+ if (!existsSync(BANKR_CACHE_DIR)) {
120
+ mkdirSync(BANKR_CACHE_DIR, { recursive: true });
121
+ }
122
+ writeFileSync(cachePath, JSON.stringify({ address, timestamp: Date.now() }));
123
+ console.log(`[BankrBackend] Cached address to ${cachePath}`);
124
+ } catch (e) {
125
+ console.warn(`[BankrBackend] Failed to cache address: ${e}`);
126
+ }
127
+ }
128
+
129
+ async getAddress(): Promise<Address> {
130
+ if (this.cachedAddress) return this.cachedAddress;
131
+
132
+ // Query Bankr for the wallet address
133
+ console.log('[BankrBackend] Fetching wallet address from Bankr...');
134
+
135
+ try {
136
+ const response = await this.submitAndWait('What is my EVM wallet address?');
137
+
138
+ // Extract address from response
139
+ const addressMatch = response.match(/0x[a-fA-F0-9]{40}/);
140
+ if (!addressMatch) {
141
+ console.warn('[BankrBackend] Could not parse address from response:', response.slice(0, 100));
142
+ throw new Error('[BankrBackend] Could not determine wallet address from Bankr');
143
+ }
144
+
145
+ this.cachedAddress = addressMatch[0] as Address;
146
+
147
+ // Save to disk for next time
148
+ this.saveCachedAddress(this.cachedAddress);
149
+
150
+ console.log(`[BankrBackend] Wallet address: ${this.cachedAddress}`);
151
+
152
+ return this.cachedAddress;
153
+ } catch (error) {
154
+ console.error('[BankrBackend] Failed to get address:', error);
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Get the Solana wallet address from Bankr
161
+ */
162
+ async getSolanaAddress(): Promise<string | null> {
163
+ if (this.cachedSolanaAddress) return this.cachedSolanaAddress;
164
+
165
+ // Check for cached address on disk
166
+ const cachePath = `${process.env.HOME}/.openclaw/cache/bankr-${this.nickname}-solana-address.json`;
167
+ try {
168
+ const fs = await import('fs');
169
+ if (fs.existsSync(cachePath)) {
170
+ const cached = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
171
+ if (cached.address && Date.now() - cached.timestamp < 86400000) {
172
+ this.cachedSolanaAddress = cached.address;
173
+ console.log(`[BankrBackend] Loaded cached Solana address: ${this.cachedSolanaAddress}`);
174
+ return this.cachedSolanaAddress;
175
+ }
176
+ }
177
+ } catch (e) {
178
+ // Cache miss, continue to query
179
+ }
180
+
181
+ console.log('[BankrBackend] Fetching Solana wallet address from Bankr...');
182
+
183
+ try {
184
+ const response = await this.submitAndWait('What is my Solana wallet address?');
185
+
186
+ // Solana addresses are base58, typically 32-44 chars, no 0x prefix
187
+ const solanaMatch = response.match(/[1-9A-HJ-NP-Za-km-z]{32,44}/);
188
+ if (!solanaMatch) {
189
+ console.warn('[BankrBackend] Could not parse Solana address from response');
190
+ return null;
191
+ }
192
+
193
+ this.cachedSolanaAddress = solanaMatch[0];
194
+ console.log(`[BankrBackend] Solana address: ${this.cachedSolanaAddress}`);
195
+
196
+ // Cache to disk
197
+ try {
198
+ const fs = await import('fs');
199
+ const path = await import('path');
200
+ const cacheDir = path.dirname(cachePath);
201
+ if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
202
+ fs.writeFileSync(cachePath, JSON.stringify({ address: this.cachedSolanaAddress, timestamp: Date.now() }));
203
+ } catch (e) {
204
+ console.warn('[BankrBackend] Failed to cache Solana address:', e);
205
+ }
206
+
207
+ return this.cachedSolanaAddress;
208
+ } catch (error) {
209
+ console.error('[BankrBackend] Failed to get Solana address:', error);
210
+ return null;
211
+ }
212
+ }
213
+
214
+ supportsChain(chainId: string): boolean {
215
+ // Normalize chain ID to handle SODAX format (0x2105.base -> base)
216
+ return isBankrSupportedChain(chainId);
217
+ }
218
+
219
+ async isReady(): Promise<boolean> {
220
+ if (!this.apiKey) return false;
221
+
222
+ try {
223
+ // Test API connectivity
224
+ const response = await fetch(`${this.apiUrl}/agent/prompt`, {
225
+ method: 'POST',
226
+ headers: {
227
+ 'X-API-Key': this.apiKey,
228
+ 'Content-Type': 'application/json',
229
+ },
230
+ body: JSON.stringify({ prompt: 'ping' }),
231
+ });
232
+
233
+ return response.status !== 503 && response.status !== 502;
234
+ } catch {
235
+ return false;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Send raw transaction via Bankr
241
+ * Uses the arbitrary transaction format
242
+ */
243
+ async sendRawTransaction(tx: RawTransaction): Promise<Hash> {
244
+ console.log(`[BankrBackend] Sending raw transaction`);
245
+ console.log(`[BankrBackend] To: ${tx.to}`);
246
+ console.log(`[BankrBackend] Chain: ${tx.chainId}`);
247
+ console.log(`[BankrBackend] Value: ${tx.value}`);
248
+ console.log(`[BankrBackend] Data: ${tx.data.slice(0, 20)}...`);
249
+
250
+ // Format as documented in arbitrary-transaction.md
251
+ const txJson = JSON.stringify({
252
+ to: tx.to,
253
+ data: tx.data,
254
+ value: tx.value,
255
+ chainId: tx.chainId,
256
+ }, null, 2);
257
+
258
+ // Use the documented prompt format
259
+ const prompt = `Submit this transaction:
260
+ ${txJson}`;
261
+
262
+ console.log(`[BankrBackend] Submitting to Bankr API...`);
263
+
264
+ const result = await this.submitAndWaitForJob(prompt);
265
+
266
+ // Extract transaction hash from response
267
+ const txHash = this.extractTransactionHash(result);
268
+
269
+ if (!txHash) {
270
+ const errorMsg = result.error || result.response || 'Unknown error';
271
+ throw new Error(`[BankrBackend] Transaction failed: ${errorMsg}`);
272
+ }
273
+
274
+ console.log(`[BankrBackend] Transaction hash: ${txHash}`);
275
+ return txHash;
276
+ }
277
+
278
+ /**
279
+ * Submit prompt and wait for text response
280
+ */
281
+ private async submitAndWait(prompt: string): Promise<string> {
282
+ const result = await this.submitAndWaitForJob(prompt);
283
+ return result.response || '';
284
+ }
285
+
286
+ /**
287
+ * Submit prompt and wait for job completion
288
+ */
289
+ private async submitAndWaitForJob(prompt: string): Promise<BankrJobStatusResponse> {
290
+ // Submit job
291
+ const submitResponse = await fetch(`${this.apiUrl}/agent/prompt`, {
292
+ method: 'POST',
293
+ headers: {
294
+ 'X-API-Key': this.apiKey,
295
+ 'Content-Type': 'application/json',
296
+ },
297
+ body: JSON.stringify({ prompt }),
298
+ });
299
+
300
+ if (!submitResponse.ok) {
301
+ const error = await submitResponse.text();
302
+ throw new Error(`[BankrBackend] Failed to submit job: ${submitResponse.status} ${error}`);
303
+ }
304
+
305
+ const submitData = await submitResponse.json() as BankrJobSubmitResponse;
306
+
307
+ if (!submitData.success || !submitData.jobId) {
308
+ throw new Error(`[BankrBackend] Invalid job response: ${JSON.stringify(submitData)}`);
309
+ }
310
+
311
+ const jobId = submitData.jobId;
312
+ console.log(`[BankrBackend] Job submitted: ${jobId}`);
313
+
314
+ // Poll for completion
315
+ let lastStatus = '';
316
+
317
+ for (let attempt = 0; attempt < this.maxPollAttempts; attempt++) {
318
+ await this.sleep(this.pollIntervalMs);
319
+
320
+ const statusResponse = await fetch(`${this.apiUrl}/agent/job/${jobId}`, {
321
+ method: 'GET',
322
+ headers: { 'X-API-Key': this.apiKey },
323
+ });
324
+
325
+ if (!statusResponse.ok) {
326
+ const error = await statusResponse.text();
327
+ throw new Error(`[BankrBackend] Failed to get job status: ${statusResponse.status} ${error}`);
328
+ }
329
+
330
+ const result = await statusResponse.json() as BankrJobStatusResponse;
331
+
332
+ // Log status changes
333
+ if (result.status !== lastStatus) {
334
+ console.log(`[BankrBackend] Job ${jobId}: ${result.status}`);
335
+ lastStatus = result.status;
336
+ }
337
+
338
+ // Log progress updates
339
+ if (result.statusUpdates && result.statusUpdates.length > 0) {
340
+ const lastUpdate = result.statusUpdates[result.statusUpdates.length - 1];
341
+ console.log(`[BankrBackend] Progress: ${lastUpdate.message}`);
342
+ }
343
+
344
+ // Check terminal states
345
+ switch (result.status) {
346
+ case 'completed':
347
+ return result;
348
+ case 'failed':
349
+ throw new Error(`[BankrBackend] Job failed: ${result.error || 'Unknown error'}`);
350
+ case 'cancelled':
351
+ throw new Error(`[BankrBackend] Job was cancelled`);
352
+ }
353
+ }
354
+
355
+ throw new Error(`[BankrBackend] Job ${jobId} timed out`);
356
+ }
357
+
358
+ /**
359
+ * Extract transaction hash from Bankr response
360
+ */
361
+ private extractTransactionHash(result: BankrJobStatusResponse): Hash | null {
362
+ // Check richData for transaction info
363
+ if (result.richData) {
364
+ for (const item of result.richData) {
365
+ if (item.transactionHash) return item.transactionHash as Hash;
366
+ if (item.txHash) return item.txHash as Hash;
367
+ if (item.hash) return item.hash as Hash;
368
+ }
369
+ }
370
+
371
+ // Try to extract from response text
372
+ if (result.response) {
373
+ // Look for 0x + 64 hex chars
374
+ const hashMatch = result.response.match(/0x[a-fA-F0-9]{64}/);
375
+ if (hashMatch) return hashMatch[0] as Hash;
376
+
377
+ // Check for failure indicators
378
+ if (result.response.toLowerCase().includes('reverted') ||
379
+ result.response.toLowerCase().includes('failed') ||
380
+ result.response.toLowerCase().includes('insufficient')) {
381
+ console.error(`[BankrBackend] Transaction failed: ${result.response}`);
382
+ return null;
383
+ }
384
+ }
385
+
386
+ return null;
387
+ }
388
+
389
+ private sleep(ms: number): Promise<void> {
390
+ return new Promise(resolve => setTimeout(resolve, ms));
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Create a Bankr backend from API key
396
+ */
397
+ export function createBankrBackend(config: BankrBackendConfig): BankrBackend {
398
+ return new BankrBackend(config);
399
+ }