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,267 @@
1
+ /**
2
+ * Amped Wallet Provider
3
+ *
4
+ * Custom wallet provider implementing IEvmWalletProvider from @sodax/types.
5
+ *
6
+ * This replaces wallet-sdk-core's EvmWalletProvider with a more flexible
7
+ * implementation that:
8
+ * 1. Supports all chains including LightLink and HyperEVM
9
+ * 2. Has pluggable backends (local keys, Bankr, etc.)
10
+ * 3. Provides a unified interface for the SODAX SDK
11
+ *
12
+ * Architecture:
13
+ * \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
14
+ * \u2502 AmpedWalletProvider \u2502
15
+ * \u2502 (implements IEvmWalletProvider) \u2502
16
+ * \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
17
+ * \u2502 - SDK-compatible interface \u2502
18
+ * \u2502 - Chain resolution (all chains) \u2502
19
+ * \u2502 - Transaction formatting \u2502
20
+ * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
21
+ * \u2502
22
+ * \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
23
+ * \u25bc \u25bc
24
+ * \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
25
+ * \u2502 LocalKeyBack. \u2502 \u2502 BankrBackend \u2502
26
+ * \u2502 (evm-wallet) \u2502 \u2502 (API calls) \u2502
27
+ * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
28
+ */
29
+
30
+ import {
31
+ createPublicClient,
32
+ http,
33
+ type Hash,
34
+ type Address,
35
+ } from 'viem';
36
+ import type {
37
+ IEvmWalletProvider,
38
+ EvmRawTransaction,
39
+ EvmRawTransactionReceipt
40
+ } from '@sodax/types';
41
+ import type {
42
+ IWalletBackend,
43
+ WalletBackendConfig,
44
+ WalletBackendType,
45
+ IAmpedWalletProvider,
46
+ LocalKeyBackendConfig,
47
+ BankrBackendConfig,
48
+ } from './types';
49
+ import { LocalKeyBackend, createLocalKeyBackend } from './LocalKeyBackend';
50
+ import { BankrBackend, createBankrBackend } from './BankrBackend';
51
+ import { getViemChain, getDefaultRpcUrl, resolveChainId } from './chainConfig';
52
+
53
+ /**
54
+ * Amped Wallet Provider
55
+ *
56
+ * A drop-in replacement for wallet-sdk-core's EvmWalletProvider
57
+ * that supports all SODAX chains including LightLink and HyperEVM.
58
+ */
59
+ export class AmpedWalletProvider implements IAmpedWalletProvider {
60
+ readonly publicClient: ReturnType<typeof createPublicClient>;
61
+
62
+ private readonly backend: IWalletBackend;
63
+ private readonly chainId: number;
64
+
65
+ private constructor(backend: IWalletBackend, publicClient: ReturnType<typeof createPublicClient>) {
66
+ this.backend = backend;
67
+ this.publicClient = publicClient;
68
+ this.chainId = backend.getChainId();
69
+
70
+ console.log(`[AmpedWalletProvider] Initialized with ${backend.type} backend`);
71
+ console.log(`[AmpedWalletProvider] Chain ID: ${this.chainId}`);
72
+ }
73
+
74
+ /**
75
+ * Create an AmpedWalletProvider with a local key backend
76
+ *
77
+ * @param config - Configuration matching EvmWalletProvider's PrivateKeyEvmWalletConfig
78
+ * @returns AmpedWalletProvider instance
79
+ */
80
+ static async fromPrivateKey(config: {
81
+ privateKey: `0x${string}`;
82
+ chainId: string | number;
83
+ rpcUrl?: string;
84
+ }): Promise<AmpedWalletProvider> {
85
+ const chainId = resolveChainId(config.chainId);
86
+ const rpcUrl = config.rpcUrl || getDefaultRpcUrl(chainId);
87
+ const chain = getViemChain(chainId);
88
+
89
+ // Create backend
90
+ const backend = await createLocalKeyBackend({
91
+ type: 'localKey',
92
+ privateKey: config.privateKey,
93
+ chainId: config.chainId,
94
+ rpcUrl,
95
+ });
96
+
97
+ // Use the backend's public client
98
+ const publicClient = backend.getPublicClient() as any;
99
+
100
+ return new AmpedWalletProvider(backend, publicClient);
101
+ }
102
+
103
+ /**
104
+ * Create an AmpedWalletProvider with a Bankr backend
105
+ *
106
+ * @param config - Bankr backend configuration
107
+ * @returns AmpedWalletProvider instance
108
+ */
109
+ static async fromBankr(config: {
110
+ bankrApiUrl: string;
111
+ bankrApiKey: string;
112
+ userAddress: Address;
113
+ chainId: string | number;
114
+ rpcUrl?: string;
115
+ policy?: BankrBackendConfig['policy'];
116
+ }): Promise<AmpedWalletProvider> {
117
+ const chainId = resolveChainId(config.chainId);
118
+ const rpcUrl = config.rpcUrl || getDefaultRpcUrl(chainId);
119
+ const chain = getViemChain(chainId);
120
+
121
+ // Create backend
122
+ const backend = await createBankrBackend({
123
+ type: 'bankr',
124
+ bankrApiUrl: config.bankrApiUrl,
125
+ bankrApiKey: config.bankrApiKey,
126
+ userAddress: config.userAddress,
127
+ chainId: config.chainId,
128
+ rpcUrl,
129
+ policy: config.policy,
130
+ });
131
+
132
+ // Create public client (for read operations)
133
+ // Bankr backend doesn't have its own public client
134
+ const publicClient = createPublicClient({
135
+ chain,
136
+ transport: http(rpcUrl),
137
+ }) as any; // Type cast needed due to viem's strict typing
138
+
139
+ return new AmpedWalletProvider(backend, publicClient);
140
+ }
141
+
142
+ /**
143
+ * Create from generic backend configuration
144
+ */
145
+ static async fromConfig(config: WalletBackendConfig): Promise<AmpedWalletProvider> {
146
+ switch (config.type) {
147
+ case 'localKey':
148
+ return AmpedWalletProvider.fromPrivateKey({
149
+ privateKey: (config as LocalKeyBackendConfig).privateKey,
150
+ chainId: config.chainId,
151
+ rpcUrl: config.rpcUrl,
152
+ });
153
+
154
+ case 'bankr':
155
+ const bankrConfig = config as BankrBackendConfig;
156
+ return AmpedWalletProvider.fromBankr({
157
+ bankrApiUrl: bankrConfig.bankrApiUrl,
158
+ bankrApiKey: bankrConfig.bankrApiKey,
159
+ userAddress: bankrConfig.userAddress,
160
+ chainId: config.chainId,
161
+ rpcUrl: config.rpcUrl,
162
+ policy: bankrConfig.policy,
163
+ });
164
+
165
+ default:
166
+ throw new Error(`Unsupported backend type: ${(config as any).type}`);
167
+ }
168
+ }
169
+
170
+ // ===== IEvmWalletProvider Implementation =====
171
+
172
+ /**
173
+ * Get the wallet address
174
+ */
175
+ async getWalletAddress(): Promise<Address> {
176
+ return this.backend.getAddress();
177
+ }
178
+
179
+ /**
180
+ * Send a transaction
181
+ *
182
+ * Converts SDK's EvmRawTransaction format to internal format
183
+ * and delegates to the backend.
184
+ */
185
+ async sendTransaction(evmRawTx: EvmRawTransaction): Promise<Hash> {
186
+ console.log(`[AmpedWalletProvider] sendTransaction`);
187
+ console.log(`[AmpedWalletProvider] From: ${evmRawTx.from}`);
188
+ console.log(`[AmpedWalletProvider] To: ${evmRawTx.to}`);
189
+ console.log(`[AmpedWalletProvider] Value: ${evmRawTx.value}`);
190
+
191
+ return this.backend.sendTransaction({
192
+ to: evmRawTx.to,
193
+ value: evmRawTx.value,
194
+ data: evmRawTx.data,
195
+ });
196
+ }
197
+
198
+ /**
199
+ * Wait for transaction receipt
200
+ *
201
+ * Converts internal receipt format to SDK's EvmRawTransactionReceipt format.
202
+ */
203
+ async waitForTransactionReceipt(txHash: Hash): Promise<EvmRawTransactionReceipt> {
204
+ console.log(`[AmpedWalletProvider] waitForTransactionReceipt: ${txHash}`);
205
+
206
+ const receipt = await this.backend.waitForTransaction(txHash);
207
+
208
+ // Convert to SDK format
209
+ return {
210
+ transactionHash: receipt.transactionHash,
211
+ transactionIndex: '0x0', // Not tracked in our simplified receipt
212
+ blockHash: receipt.blockHash,
213
+ blockNumber: `0x${receipt.blockNumber.toString(16)}`,
214
+ from: receipt.from,
215
+ to: receipt.to,
216
+ cumulativeGasUsed: '0x0', // Not tracked
217
+ gasUsed: `0x${receipt.gasUsed.toString(16)}`,
218
+ contractAddress: null, // Would need to check if this was a deployment
219
+ logs: receipt.logs.map(log => ({
220
+ address: log.address,
221
+ topics: log.topics as [`0x${string}`, ...`0x${string}`[]] | [],
222
+ data: log.data,
223
+ blockHash: receipt.blockHash,
224
+ blockNumber: `0x${receipt.blockNumber.toString(16)}`,
225
+ logIndex: '0x0',
226
+ transactionHash: receipt.transactionHash,
227
+ transactionIndex: '0x0',
228
+ removed: false,
229
+ })),
230
+ logsBloom: '0x',
231
+ status: receipt.status === 'success' ? '0x1' : '0x0',
232
+ };
233
+ }
234
+
235
+ // ===== IAmpedWalletProvider Extensions =====
236
+
237
+ /**
238
+ * Get the underlying backend
239
+ */
240
+ getBackend(): IWalletBackend {
241
+ return this.backend;
242
+ }
243
+
244
+ /**
245
+ * Get the backend type
246
+ */
247
+ getBackendType(): WalletBackendType {
248
+ return this.backend.type;
249
+ }
250
+
251
+ /**
252
+ * Check if ready for transactions
253
+ */
254
+ async isReady(): Promise<boolean> {
255
+ return this.backend.isReady();
256
+ }
257
+
258
+ /**
259
+ * Get chain ID
260
+ */
261
+ getChainId(): number {
262
+ return this.chainId;
263
+ }
264
+ }
265
+
266
+ // Re-export for convenience
267
+ export type { IAmpedWalletProvider };
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Bankr Backend - Transaction Execution Layer
3
+ *
4
+ * CRITICAL: This backend is for EXECUTION ONLY, not routing.
5
+ *
6
+ * Architecture:
7
+ * SODAX SDK (routing) → BankrBackend (execution) → Blockchain
8
+ *
9
+ * What Bankr DOES:
10
+ * ✓ Signs the pre-computed transaction from SODAX
11
+ * ✓ Submits to blockchain via Bankr API
12
+ * ✓ Returns transaction hash
13
+ *
14
+ * What Bankr does NOT do:
15
+ * ✗ NO routing decisions
16
+ * ✗ NO DeFi protocol selection
17
+ * ✗ NO swap optimization
18
+ * ✗ NO interpretation of intent
19
+ *
20
+ * The SODAX SDK always handles routing logic. Bankr receives the exact
21
+ * transaction data (to, data, value, chainId) and submits it verbatim.
22
+ *
23
+ * @see SKILL.md "Transaction Execution Architecture" section
24
+ */
25
+
26
+ import type { Hash, Address } from 'viem';
27
+ import type {
28
+ IWalletBackend,
29
+ BankrBackendConfig,
30
+ TransactionRequest,
31
+ TransactionReceipt
32
+ } from './types';
33
+ import { resolveChainId } from './chainConfig';
34
+
35
+ /**
36
+ * Serialize error objects for readable error messages
37
+ */
38
+ function serializeError(error: unknown): string {
39
+ if (error instanceof Error) return error.message;
40
+ if (typeof error === 'string') return error;
41
+ try {
42
+ return JSON.stringify(error, (k, v) => typeof v === 'bigint' ? v.toString() : v, 2);
43
+ } catch {
44
+ return String(error);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Bankr Agent API response types
50
+ */
51
+ interface BankrJobSubmitResponse {
52
+ success: boolean;
53
+ jobId: string;
54
+ status: 'pending';
55
+ message: string;
56
+ }
57
+
58
+ interface BankrJobStatusResponse {
59
+ success: boolean;
60
+ jobId: string;
61
+ status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';
62
+ prompt: string;
63
+ response?: string;
64
+ error?: string;
65
+ richData?: Array<{ type?: string; [key: string]: unknown }>;
66
+ statusUpdates?: Array<{ message: string; timestamp: string }>;
67
+ createdAt: string;
68
+ completedAt?: string;
69
+ processingTime?: number;
70
+ }
71
+
72
+ /**
73
+ * Chain ID to chain name mapping for Bankr prompts
74
+ */
75
+ const CHAIN_NAMES: Record<number, string> = {
76
+ 1: 'ethereum',
77
+ 8453: 'base',
78
+ 137: 'polygon',
79
+ 42161: 'arbitrum',
80
+ 10: 'optimism',
81
+ 1890: 'lightlink',
82
+ 146: 'sonic',
83
+ };
84
+
85
+ /**
86
+ * Bankr execution backend
87
+ *
88
+ * Delegates transaction execution to Bankr's Agent API.
89
+ * The agent never has direct access to private keys.
90
+ */
91
+ export class BankrBackend implements IWalletBackend {
92
+ readonly type = 'bankr' as const;
93
+
94
+ private readonly apiUrl: string;
95
+ private readonly apiKey: string;
96
+ private readonly userAddress: Address;
97
+ private readonly chainId: number;
98
+ private readonly policy?: BankrBackendConfig['policy'];
99
+
100
+ // Polling configuration
101
+ private readonly pollIntervalMs = 2000;
102
+ private readonly maxPollAttempts = 150; // 5 minutes max
103
+
104
+ constructor(config: BankrBackendConfig) {
105
+ this.apiUrl = config.bankrApiUrl || 'https://api.bankr.bot';
106
+ this.apiKey = config.bankrApiKey;
107
+ this.userAddress = config.userAddress;
108
+ this.chainId = resolveChainId(config.chainId);
109
+ this.policy = config.policy;
110
+
111
+ console.log(`[BankrBackend] Initialized for chain ${this.chainId}`);
112
+ console.log(`[BankrBackend] User address: ${this.userAddress}`);
113
+ console.log(`[BankrBackend] API URL: ${this.apiUrl}`);
114
+ }
115
+
116
+ /**
117
+ * Get the wallet address (Bankr-provisioned)
118
+ */
119
+ async getAddress(): Promise<Address> {
120
+ return this.userAddress;
121
+ }
122
+
123
+ /**
124
+ * Send a transaction via Bankr Agent API
125
+ *
126
+ * Formats the transaction as a natural language prompt and submits
127
+ * to Bankr's async job system.
128
+ */
129
+ async sendTransaction(tx: TransactionRequest): Promise<Hash> {
130
+ console.log(`[BankrBackend] Sending transaction via Bankr API`);
131
+ console.log(`[BankrBackend] To: ${tx.to}`);
132
+ console.log(`[BankrBackend] Value: ${tx.value || 0n}`);
133
+ console.log(`[BankrBackend] Data: ${tx.data ? tx.data.slice(0, 20) + '...' : '0x'}`);
134
+
135
+ // Validate against policy
136
+ if (this.policy) {
137
+ await this.validatePolicy(tx);
138
+ }
139
+
140
+ // Format transaction as JSON for Bankr prompt
141
+ const txJson = JSON.stringify({
142
+ to: tx.to,
143
+ data: tx.data || '0x',
144
+ value: (tx.value || 0n).toString(),
145
+ chainId: this.chainId,
146
+ });
147
+
148
+ // Create natural language prompt for Bankr
149
+ const prompt = `Submit this transaction: ${txJson}`;
150
+
151
+ console.log(`[BankrBackend] Submitting prompt to Bankr API`);
152
+
153
+ // Submit job to Bankr
154
+ const jobId = await this.submitJob(prompt);
155
+ console.log(`[BankrBackend] Job submitted: ${jobId}`);
156
+
157
+ // Poll for completion
158
+ const result = await this.pollJobUntilComplete(jobId);
159
+
160
+ // Extract transaction hash from response
161
+ const txHash = this.extractTransactionHash(result);
162
+
163
+ if (!txHash) {
164
+ throw new Error(`[BankrBackend] Transaction failed: ${serializeError(result.response || result.error) || 'Unknown error'}`);
165
+ }
166
+
167
+ console.log(`[BankrBackend] Transaction hash: ${txHash}`);
168
+ return txHash;
169
+ }
170
+
171
+ /**
172
+ * Wait for transaction confirmation
173
+ *
174
+ * Note: With Bankr, the transaction is already confirmed when we get
175
+ * the response. This method exists for interface compatibility but
176
+ * returns a minimal receipt.
177
+ */
178
+ async waitForTransaction(txHash: Hash): Promise<TransactionReceipt> {
179
+ console.log(`[BankrBackend] waitForTransaction called for: ${txHash}`);
180
+
181
+ // Bankr transactions are confirmed when the job completes
182
+ // We return a minimal receipt since we don't have full details
183
+ return {
184
+ transactionHash: txHash,
185
+ blockNumber: 0n, // Unknown - would need to query chain
186
+ blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000' as Hash,
187
+ from: this.userAddress,
188
+ to: null,
189
+ gasUsed: 0n,
190
+ status: 'success',
191
+ logs: [],
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Check if backend is ready
197
+ *
198
+ * Verifies Bankr API connectivity with a simple balance query.
199
+ */
200
+ async isReady(): Promise<boolean> {
201
+ if (!this.apiUrl || !this.apiKey || !this.userAddress) {
202
+ return false;
203
+ }
204
+
205
+ try {
206
+ // Test API connectivity with a simple query
207
+ const response = await fetch(`${this.apiUrl}/agent/prompt`, {
208
+ method: 'POST',
209
+ headers: {
210
+ 'X-API-Key': this.apiKey,
211
+ 'Content-Type': 'application/json',
212
+ },
213
+ body: JSON.stringify({ prompt: 'ping' }),
214
+ });
215
+
216
+ // Even a 4xx error means API is reachable
217
+ return response.status !== 503 && response.status !== 502;
218
+ } catch (error) {
219
+ console.error('[BankrBackend] Connectivity check failed:', error);
220
+ return false;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Get the chain ID
226
+ */
227
+ getChainId(): number {
228
+ return this.chainId;
229
+ }
230
+
231
+ /**
232
+ * Submit a job to Bankr Agent API
233
+ */
234
+ private async submitJob(prompt: string): Promise<string> {
235
+ const response = await fetch(`${this.apiUrl}/agent/prompt`, {
236
+ method: 'POST',
237
+ headers: {
238
+ 'X-API-Key': this.apiKey,
239
+ 'Content-Type': 'application/json',
240
+ },
241
+ body: JSON.stringify({ prompt }),
242
+ });
243
+
244
+ if (!response.ok) {
245
+ const error = await response.text();
246
+ throw new Error(`[BankrBackend] Failed to submit job: ${response.status} ${error}`);
247
+ }
248
+
249
+ const data = await response.json() as BankrJobSubmitResponse;
250
+
251
+ if (!data.success || !data.jobId) {
252
+ throw new Error(`[BankrBackend] Invalid job submission response: ${JSON.stringify(data)}`);
253
+ }
254
+
255
+ return data.jobId;
256
+ }
257
+
258
+ /**
259
+ * Poll for job completion
260
+ */
261
+ private async pollJobUntilComplete(jobId: string): Promise<BankrJobStatusResponse> {
262
+ let lastStatus = '';
263
+
264
+ for (let attempt = 0; attempt < this.maxPollAttempts; attempt++) {
265
+ await this.sleep(this.pollIntervalMs);
266
+
267
+ const result = await this.getJobStatus(jobId);
268
+
269
+ // Log status changes
270
+ if (result.status !== lastStatus) {
271
+ console.log(`[BankrBackend] Job ${jobId} status: ${result.status}`);
272
+ lastStatus = result.status;
273
+ }
274
+
275
+ // Log status updates
276
+ if (result.statusUpdates && result.statusUpdates.length > 0) {
277
+ const lastUpdate = result.statusUpdates[result.statusUpdates.length - 1];
278
+ console.log(`[BankrBackend] Progress: ${lastUpdate.message}`);
279
+ }
280
+
281
+ // Check for terminal states
282
+ switch (result.status) {
283
+ case 'completed':
284
+ return result;
285
+ case 'failed':
286
+ throw new Error(`[BankrBackend] Job failed: ${serializeError(result.error) || 'Unknown error'}`);
287
+ case 'cancelled':
288
+ throw new Error(`[BankrBackend] Job was cancelled`);
289
+ case 'pending':
290
+ case 'processing':
291
+ // Continue polling
292
+ break;
293
+ default:
294
+ console.warn(`[BankrBackend] Unknown status: ${result.status}`);
295
+ }
296
+ }
297
+
298
+ throw new Error(`[BankrBackend] Job ${jobId} timed out after ${this.maxPollAttempts * this.pollIntervalMs / 1000} seconds`);
299
+ }
300
+
301
+ /**
302
+ * Get job status from Bankr API
303
+ */
304
+ private async getJobStatus(jobId: string): Promise<BankrJobStatusResponse> {
305
+ const response = await fetch(`${this.apiUrl}/agent/job/${jobId}`, {
306
+ method: 'GET',
307
+ headers: {
308
+ 'X-API-Key': this.apiKey,
309
+ },
310
+ });
311
+
312
+ if (!response.ok) {
313
+ const error = await response.text();
314
+ throw new Error(`[BankrBackend] Failed to get job status: ${response.status} ${error}`);
315
+ }
316
+
317
+ return await response.json() as BankrJobStatusResponse;
318
+ }
319
+
320
+ /**
321
+ * Extract transaction hash from Bankr response
322
+ *
323
+ * The response may contain the tx hash in various formats:
324
+ * - In richData array
325
+ * - In the response text (e.g., "Transaction hash: 0x...")
326
+ */
327
+ private extractTransactionHash(result: BankrJobStatusResponse): Hash | null {
328
+ // Check richData for transaction info
329
+ if (result.richData) {
330
+ for (const item of result.richData) {
331
+ if (item.transactionHash) {
332
+ return item.transactionHash as Hash;
333
+ }
334
+ if (item.txHash) {
335
+ return item.txHash as Hash;
336
+ }
337
+ if (item.hash) {
338
+ return item.hash as Hash;
339
+ }
340
+ }
341
+ }
342
+
343
+ // Try to extract from response text
344
+ if (result.response) {
345
+ // Look for hex transaction hash pattern (0x followed by 64 hex chars)
346
+ const hashMatch = result.response.match(/0x[a-fA-F0-9]{64}/);
347
+ if (hashMatch) {
348
+ return hashMatch[0] as Hash;
349
+ }
350
+
351
+ // Check if response indicates failure
352
+ if (result.response.toLowerCase().includes('reverted') ||
353
+ result.response.toLowerCase().includes('failed') ||
354
+ result.response.toLowerCase().includes('insufficient')) {
355
+ console.error(`[BankrBackend] Transaction failed: ${result.response}`);
356
+ return null;
357
+ }
358
+ }
359
+
360
+ return null;
361
+ }
362
+
363
+ /**
364
+ * Validate transaction against policy
365
+ */
366
+ private async validatePolicy(tx: TransactionRequest): Promise<void> {
367
+ if (!this.policy) return;
368
+
369
+ // Check max value per transaction
370
+ if (this.policy.maxValuePerTx && tx.value && tx.value > this.policy.maxValuePerTx) {
371
+ throw new Error(
372
+ `Transaction value ${tx.value} exceeds max allowed ${this.policy.maxValuePerTx}`
373
+ );
374
+ }
375
+
376
+ // Check allowed contracts
377
+ if (this.policy.allowedContracts && this.policy.allowedContracts.length > 0) {
378
+ if (!this.policy.allowedContracts.includes(tx.to)) {
379
+ throw new Error(
380
+ `Contract ${tx.to} is not in the allowed contracts list`
381
+ );
382
+ }
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Sleep helper
388
+ */
389
+ private sleep(ms: number): Promise<void> {
390
+ return new Promise(resolve => setTimeout(resolve, ms));
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Create a BankrBackend from configuration
396
+ */
397
+ export async function createBankrBackend(config: BankrBackendConfig): Promise<BankrBackend> {
398
+ const backend = new BankrBackend(config);
399
+
400
+ // Verify connectivity
401
+ const ready = await backend.isReady();
402
+ if (!ready) {
403
+ console.warn('[BankrBackend] Backend created but connectivity check failed');
404
+ }
405
+
406
+ return backend;
407
+ }