@dainprotocol/service-sdk 2.0.53 → 2.0.54

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 (105) hide show
  1. package/README.md +78 -0
  2. package/dist/__tests__/api-sdk.test.d.ts +1 -0
  3. package/dist/__tests__/api-sdk.test.js +102 -0
  4. package/dist/__tests__/api-sdk.test.js.map +1 -0
  5. package/dist/__tests__/auth.test.d.ts +1 -0
  6. package/dist/__tests__/auth.test.js +110 -0
  7. package/dist/__tests__/auth.test.js.map +1 -0
  8. package/dist/__tests__/citations-plugin.test.d.ts +1 -0
  9. package/dist/__tests__/citations-plugin.test.js +491 -0
  10. package/dist/__tests__/citations-plugin.test.js.map +1 -0
  11. package/dist/__tests__/context-behavior.test.d.ts +1 -0
  12. package/dist/__tests__/context-behavior.test.js +290 -0
  13. package/dist/__tests__/context-behavior.test.js.map +1 -0
  14. package/dist/__tests__/convertToVercelTool.test.d.ts +1 -0
  15. package/dist/__tests__/convertToVercelTool.test.js +527 -0
  16. package/dist/__tests__/convertToVercelTool.test.js.map +1 -0
  17. package/dist/__tests__/core.test.d.ts +1 -0
  18. package/dist/__tests__/core.test.js +154 -0
  19. package/dist/__tests__/core.test.js.map +1 -0
  20. package/dist/__tests__/crypto-plugin.test.d.ts +1 -0
  21. package/dist/__tests__/crypto-plugin.test.js +694 -0
  22. package/dist/__tests__/crypto-plugin.test.js.map +1 -0
  23. package/dist/__tests__/humanActions.test.d.ts +1 -0
  24. package/dist/__tests__/humanActions.test.js +221 -0
  25. package/dist/__tests__/humanActions.test.js.map +1 -0
  26. package/dist/__tests__/integration.test.d.ts +1 -0
  27. package/dist/__tests__/integration.test.js +1573 -0
  28. package/dist/__tests__/integration.test.js.map +1 -0
  29. package/dist/__tests__/mealMeSchemas.test.d.ts +576 -0
  30. package/dist/__tests__/mealMeSchemas.test.js +627 -0
  31. package/dist/__tests__/mealMeSchemas.test.js.map +1 -0
  32. package/dist/__tests__/oauth-context-simple.test.d.ts +1 -0
  33. package/dist/__tests__/oauth-context-simple.test.js +90 -0
  34. package/dist/__tests__/oauth-context-simple.test.js.map +1 -0
  35. package/dist/__tests__/oauth-context.test.d.ts +1 -0
  36. package/dist/__tests__/oauth-context.test.js +282 -0
  37. package/dist/__tests__/oauth-context.test.js.map +1 -0
  38. package/dist/__tests__/oauth.test.d.ts +1 -0
  39. package/dist/__tests__/oauth.test.js +378 -0
  40. package/dist/__tests__/oauth.test.js.map +1 -0
  41. package/dist/__tests__/oauth2-client-context.test.d.ts +1 -0
  42. package/dist/__tests__/oauth2-client-context.test.js +165 -0
  43. package/dist/__tests__/oauth2-client-context.test.js.map +1 -0
  44. package/dist/__tests__/oauth2-client-integration.test.d.ts +1 -0
  45. package/dist/__tests__/oauth2-client-integration.test.js +182 -0
  46. package/dist/__tests__/oauth2-client-integration.test.js.map +1 -0
  47. package/dist/__tests__/oauth2-client-simple.test.d.ts +1 -0
  48. package/dist/__tests__/oauth2-client-simple.test.js +144 -0
  49. package/dist/__tests__/oauth2-client-simple.test.js.map +1 -0
  50. package/dist/__tests__/oauth2-context.test.d.ts +1 -0
  51. package/dist/__tests__/oauth2-context.test.js +201 -0
  52. package/dist/__tests__/oauth2-context.test.js.map +1 -0
  53. package/dist/__tests__/oauth2-datasource.test.d.ts +1 -0
  54. package/dist/__tests__/oauth2-datasource.test.js +251 -0
  55. package/dist/__tests__/oauth2-datasource.test.js.map +1 -0
  56. package/dist/__tests__/plugin.test.d.ts +1 -0
  57. package/dist/__tests__/plugin.test.js +900 -0
  58. package/dist/__tests__/plugin.test.js.map +1 -0
  59. package/dist/__tests__/processes.test.d.ts +1 -0
  60. package/dist/__tests__/processes.test.js +239 -0
  61. package/dist/__tests__/processes.test.js.map +1 -0
  62. package/dist/__tests__/streaming.test.d.ts +1 -0
  63. package/dist/__tests__/streaming.test.js +592 -0
  64. package/dist/__tests__/streaming.test.js.map +1 -0
  65. package/dist/__tests__/testEnums.d.ts +1 -0
  66. package/dist/__tests__/testEnums.js +73 -0
  67. package/dist/__tests__/testEnums.js.map +1 -0
  68. package/dist/__tests__/testOptionals.test.d.ts +1 -0
  69. package/dist/__tests__/testOptionals.test.js +83 -0
  70. package/dist/__tests__/testOptionals.test.js.map +1 -0
  71. package/dist/__tests__/types.test.d.ts +1 -0
  72. package/dist/__tests__/types.test.js +98 -0
  73. package/dist/__tests__/types.test.js.map +1 -0
  74. package/dist/client/client.d.ts +18 -5
  75. package/dist/client/client.js +25 -4
  76. package/dist/client/client.js.map +1 -1
  77. package/dist/client/types.d.ts +38 -0
  78. package/dist/client/types.js +35 -1
  79. package/dist/client/types.js.map +1 -1
  80. package/dist/service/actionable-tools.d.ts +135 -0
  81. package/dist/service/actionable-tools.js +144 -0
  82. package/dist/service/actionable-tools.js.map +1 -0
  83. package/dist/service/direct-auth-setup.d.ts +154 -0
  84. package/dist/service/direct-auth-setup.js +308 -0
  85. package/dist/service/direct-auth-setup.js.map +1 -0
  86. package/dist/service/index.d.ts +3 -1
  87. package/dist/service/index.js +10 -1
  88. package/dist/service/index.js.map +1 -1
  89. package/dist/service/nodeService.js +2 -0
  90. package/dist/service/nodeService.js.map +1 -1
  91. package/dist/service/oauth2.d.ts +40 -1
  92. package/dist/service/oauth2.js +46 -0
  93. package/dist/service/oauth2.js.map +1 -1
  94. package/dist/service/oauth2Manager.d.ts +31 -4
  95. package/dist/service/oauth2Manager.js +119 -8
  96. package/dist/service/oauth2Manager.js.map +1 -1
  97. package/dist/service/server.js +93 -19
  98. package/dist/service/server.js.map +1 -1
  99. package/dist/service/service.js +6 -1
  100. package/dist/service/service.js.map +1 -1
  101. package/dist/service/types.d.ts +59 -0
  102. package/dist/service/zod-json-converter.d.ts +14 -0
  103. package/dist/service/zod-json-converter.js +203 -0
  104. package/dist/service/zod-json-converter.js.map +1 -0
  105. package/package.json +9 -10
@@ -0,0 +1,694 @@
1
+ "use strict";
2
+ // File: src/__tests__/crypto-plugin.test.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const tslib_1 = require("tslib");
5
+ const client_auth_1 = require("@/client/client-auth");
6
+ const client_1 = require("@/client/client");
7
+ const ed25519_1 = require("@noble/curves/ed25519");
8
+ const bs58_1 = tslib_1.__importDefault(require("bs58"));
9
+ const nodeService_1 = require("@/service/nodeService");
10
+ const core_1 = require("@/service/core");
11
+ const zod_1 = require("zod");
12
+ const crypto_plugin_1 = require("@/plugins/crypto-plugin");
13
+ const cuid2_1 = require("@paralleldrive/cuid2");
14
+ describe("DAIN Framework CryptoPlugin System", () => {
15
+ // Generate test keys
16
+ const servicePrivateKey = ed25519_1.ed25519.utils.randomPrivateKey();
17
+ const servicePublicKey = ed25519_1.ed25519.getPublicKey(servicePrivateKey);
18
+ const serviceAddress = bs58_1.default.encode(servicePublicKey);
19
+ const clientPrivateKey = ed25519_1.ed25519.utils.randomPrivateKey();
20
+ // Set up client authentication
21
+ const agentAuth = new client_auth_1.DainClientAuth({
22
+ privateKeyBase58: bs58_1.default.encode(clientPrivateKey),
23
+ agentId: "agent-crypto-test",
24
+ orgId: "org-crypto-test",
25
+ });
26
+ // Create a test wallet configuration
27
+ const testWallets = [
28
+ { chain: "sol", address: "Sol11111111111111111111111111111111111111111" },
29
+ { chain: "eth", address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" },
30
+ { chain: "arb", address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" }
31
+ ];
32
+ // Create separate CryptoPlugin instances for client and service
33
+ const clientCryptoPlugin = new crypto_plugin_1.CryptoPlugin(testWallets);
34
+ const serviceCryptoPlugin = new crypto_plugin_1.CryptoPlugin(); // Service doesn't need wallet information
35
+ // Initialize the connection to the DAIN service with the client crypto plugin
36
+ const dainConnection = new client_1.DainServiceConnection("http://localhost:4485", agentAuth, {
37
+ plugins: [clientCryptoPlugin]
38
+ });
39
+ // Create a mock Solana transaction (added id field)
40
+ const mockSolanaTransaction = {
41
+ encodedTx: "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
42
+ chain: "sol",
43
+ signer: "Sol11111111111111111111111111111111111111111",
44
+ metadata: {
45
+ description: "Test SOL Transfer",
46
+ amount: 0.01,
47
+ recipient: "SolRecipient111111111111111111111111111111111"
48
+ }
49
+ };
50
+ // Create a mock Ethereum transaction (added id field)
51
+ const mockEthereumTransaction = {
52
+ encodedTx: "0xf86c0a85046c7cfe0083016dea94d1310c1e0aa1b9d5dfe80aa9ee4a8b0a48d0f2a780a46057361d00000000000000000000000000000000000000000000000000000000000003e880820a95a0f5d4c5201602ddf5110f112a85fb0cf39a85e3a3174cd237b8383670cab7a3b5a048f2be5cc9f0a0b3f22e60f709c649d5a6a4ad3d70ce44ec80b96ffe7843508c",
53
+ chain: "eth",
54
+ signer: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
55
+ metadata: {
56
+ description: "Test ETH Transfer",
57
+ amount: 0.1,
58
+ recipient: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
59
+ gasLimit: 21000
60
+ }
61
+ };
62
+ // Create a wallet context to test context with plugins
63
+ const walletContext = {
64
+ id: "wallet-context",
65
+ name: "Wallet Information",
66
+ description: "Provides information about available wallets",
67
+ getContextData: async (agentInfo, context) => {
68
+ console.log("[CONTEXT] Getting wallet information with context:", context);
69
+ // Use the unified helper method to extract wallet information
70
+ const wallets = serviceCryptoPlugin.getUserWallets(context);
71
+ console.log(`[CONTEXT] Found ${wallets.length} wallet(s) using helper method`);
72
+ if (wallets.length > 0) {
73
+ return {
74
+ connectedWallets: wallets.map(wallet => ({
75
+ chain: wallet.chain,
76
+ address: wallet.address,
77
+ shortAddress: wallet.address.substring(0, 6) + '...' + wallet.address.substring(wallet.address.length - 4)
78
+ })),
79
+ walletsCount: wallets.length,
80
+ primaryWallet: wallets[0] || null
81
+ };
82
+ }
83
+ return {
84
+ connectedWallets: [],
85
+ walletsCount: 0,
86
+ primaryWallet: null
87
+ };
88
+ }
89
+ };
90
+ // Create a balance datasource to test datasource with plugins
91
+ const walletBalanceDatasource = {
92
+ id: "wallet-balance",
93
+ name: "Wallet Balance",
94
+ description: "Get balance information for connected wallets",
95
+ type: "json",
96
+ input: zod_1.z.object({
97
+ chain: zod_1.z.enum(["sol", "eth", "arb"]).optional()
98
+ }),
99
+ getDatasource: async (agentInfo, params, context) => {
100
+ console.log("[DATASOURCE] Getting wallet balance with context:", context);
101
+ // Use the unified helper method to extract wallet information
102
+ const wallets = serviceCryptoPlugin.getUserWallets(context);
103
+ console.log(`[DATASOURCE] Found ${wallets.length} wallet(s) using helper method`);
104
+ if (wallets.length > 0) {
105
+ const targetWallets = params.chain
106
+ ? wallets.filter(wallet => wallet.chain === params.chain)
107
+ : wallets;
108
+ // If a specific chain is requested, we can also use the unified helper method to get that specific wallet
109
+ if (params.chain) {
110
+ const specificWallet = serviceCryptoPlugin.getUserWalletForChain(context, params.chain);
111
+ console.log(`[DATASOURCE] Wallet for chain ${params.chain}: ${specificWallet}`);
112
+ }
113
+ // Generate mock balances for demonstration purposes
114
+ return {
115
+ balances: targetWallets.map(wallet => ({
116
+ chain: wallet.chain,
117
+ address: wallet.address,
118
+ balance: Math.random() * 10,
119
+ token: wallet.chain === "sol" ? "SOL" : wallet.chain === "eth" ? "ETH" : "ARB",
120
+ usdValue: Math.random() * 1000
121
+ })),
122
+ totalUsdValue: Math.random() * 5000,
123
+ timestamp: Date.now()
124
+ };
125
+ }
126
+ return {
127
+ balances: [],
128
+ totalUsdValue: 0,
129
+ timestamp: Date.now()
130
+ };
131
+ }
132
+ };
133
+ // Create a test tool that uses wallet information and creates unsigned transactions
134
+ const cryptoTransferTool = (0, core_1.createTool)({
135
+ id: "crypto-transfer",
136
+ name: "Cryptocurrency Transfer Tool",
137
+ description: "Creates unsigned transactions for cryptocurrency transfers",
138
+ input: zod_1.z.object({
139
+ amount: zod_1.z.number().positive(),
140
+ chain: zod_1.z.enum(["sol", "eth", "arb"]),
141
+ recipientAddress: zod_1.z.string(),
142
+ executeSimultaneously: zod_1.z.boolean().optional(),
143
+ waitForConfirmation: zod_1.z.boolean().optional()
144
+ }),
145
+ output: zod_1.z.object({
146
+ success: zod_1.z.boolean(),
147
+ message: zod_1.z.string(),
148
+ signatureRequests: zod_1.z.array(zod_1.z.object({
149
+ txs: zod_1.z.array(zod_1.z.object({
150
+ id: zod_1.z.string(),
151
+ encodedTx: zod_1.z.string(),
152
+ chain: zod_1.z.enum(["sol", "eth", "arb"]),
153
+ signer: zod_1.z.string(),
154
+ metadata: zod_1.z.any().optional(),
155
+ ui: zod_1.z.any().optional()
156
+ })),
157
+ executeSimultaneously: zod_1.z.boolean().optional(),
158
+ toolsShouldWaitForConfirmation: zod_1.z.boolean().optional(),
159
+ ui: zod_1.z.any().optional()
160
+ })).optional()
161
+ }),
162
+ handler: async ({ amount, chain, recipientAddress, executeSimultaneously, waitForConfirmation }, agentInfo, context) => {
163
+ // Use the SERVICE-SIDE plugin unified helper to get the user's wallets
164
+ const wallets = serviceCryptoPlugin.getUserWallets(context);
165
+ console.log(`[SERVICE] Available user wallets:`, wallets);
166
+ // Get the wallet for the specified chain
167
+ const userWallet = serviceCryptoPlugin.getUserWalletForChain(context, chain);
168
+ if (!userWallet) {
169
+ return {
170
+ text: `No wallet found for ${chain} chain`,
171
+ data: {
172
+ success: false,
173
+ message: `No ${chain} wallet found for this user. Please connect a ${chain} wallet.`
174
+ },
175
+ ui: null
176
+ };
177
+ }
178
+ console.log(`[SERVICE] Using ${chain} wallet: ${userWallet} for transfer of ${amount} to ${recipientAddress}`);
179
+ // Create mock transactions based on the chain
180
+ let mockTransaction;
181
+ if (chain === "sol") {
182
+ mockTransaction = {
183
+ ...mockSolanaTransaction,
184
+ signer: userWallet,
185
+ metadata: {
186
+ ...mockSolanaTransaction.metadata,
187
+ amount: amount,
188
+ recipient: recipientAddress
189
+ },
190
+ ui: {
191
+ type: "transaction",
192
+ title: "Solana Transfer",
193
+ description: `Transfer ${amount} SOL to ${recipientAddress}`,
194
+ icon: "💸",
195
+ fields: [
196
+ { label: "Amount", value: `${amount} SOL` },
197
+ { label: "To", value: recipientAddress, truncate: true },
198
+ { label: "Network", value: "Solana", badge: "mainnet" }
199
+ ],
200
+ theme: "purple"
201
+ }
202
+ };
203
+ }
204
+ else if (chain === "eth" || chain === "arb") {
205
+ mockTransaction = {
206
+ ...mockEthereumTransaction,
207
+ chain: chain,
208
+ signer: userWallet,
209
+ metadata: {
210
+ ...mockEthereumTransaction.metadata,
211
+ amount: amount,
212
+ recipient: recipientAddress
213
+ },
214
+ ui: {
215
+ type: "transaction",
216
+ title: `${chain === "eth" ? "Ethereum" : "Arbitrum"} Transfer`,
217
+ description: `Transfer ${amount} ${chain === "eth" ? "ETH" : "ARB"} to ${recipientAddress}`,
218
+ icon: chain === "eth" ? "Ξ" : "🔷",
219
+ fields: [
220
+ { label: "Amount", value: `${amount} ${chain === "eth" ? "ETH" : "ARB"}` },
221
+ { label: "To", value: recipientAddress, truncate: true },
222
+ { label: "Network", value: chain === "eth" ? "Ethereum" : "Arbitrum", badge: chain === "eth" ? "mainnet" : "L2" },
223
+ { label: "Gas Limit", value: "21000" }
224
+ ],
225
+ theme: chain === "eth" ? "blue" : "orange"
226
+ }
227
+ };
228
+ }
229
+ else {
230
+ return {
231
+ text: `Unsupported chain: ${chain}`,
232
+ data: {
233
+ success: false,
234
+ message: `Chain ${chain} is not supported yet.`
235
+ },
236
+ ui: null
237
+ };
238
+ }
239
+ // Create a transaction with a unique ID using the service plugin helper
240
+ const transactionWithId = serviceCryptoPlugin.createTransaction(mockTransaction);
241
+ // Log the transaction ID for debugging
242
+ console.log(`[SERVICE] Created transaction with ID: ${transactionWithId.id}`);
243
+ // Create a signature request using the SERVICE-SIDE plugin helper
244
+ const signatureRequest = serviceCryptoPlugin.createTransactionRequest([transactionWithId], {
245
+ executeSimultaneously: executeSimultaneously,
246
+ toolsShouldWaitForConfirmation: waitForConfirmation,
247
+ ui: {
248
+ type: "transaction-group",
249
+ title: "Transaction Approval Required",
250
+ description: "Please review and sign the following transaction:",
251
+ style: "card",
252
+ actions: [
253
+ { label: "Sign & Send", action: "approve", variant: "primary" },
254
+ { label: "Cancel", action: "reject", variant: "secondary" }
255
+ ]
256
+ }
257
+ });
258
+ return {
259
+ text: `Created unsigned ${chain} transaction for ${amount} to ${recipientAddress}`,
260
+ data: {
261
+ success: true,
262
+ message: `Created unsigned ${chain} transaction. Please sign to complete the transfer.`,
263
+ signatureRequests: [signatureRequest]
264
+ },
265
+ ui: null
266
+ };
267
+ },
268
+ });
269
+ // Initialize the DAIN service with the SERVICE crypto plugin instance
270
+ const dainService = (0, nodeService_1.defineDAINService)({
271
+ metadata: {
272
+ title: "Crypto Plugin Test Service",
273
+ description: "A DAIN service for testing the crypto plugin",
274
+ version: "1.0.0",
275
+ author: "Test Author",
276
+ tags: ["crypto", "plugin", "test"],
277
+ },
278
+ identity: {
279
+ publicKey: bs58_1.default.encode(servicePublicKey),
280
+ agentId: "crypto-agent",
281
+ orgId: "crypto-org",
282
+ privateKey: bs58_1.default.encode(servicePrivateKey),
283
+ },
284
+ tools: [cryptoTransferTool],
285
+ plugins: [serviceCryptoPlugin], // Using service-side plugin instance
286
+ contexts: [walletContext],
287
+ datasources: [walletBalanceDatasource]
288
+ });
289
+ let server;
290
+ beforeAll(async () => {
291
+ server = await dainService.startNode({ port: 4485 });
292
+ await new Promise(resolve => setTimeout(resolve, 500)); // Give server time to start
293
+ });
294
+ afterAll(async () => {
295
+ await server.shutdown();
296
+ });
297
+ it("should assign unique IDs to transactions", async () => {
298
+ console.log("[CLIENT] Using client plugin with wallets:", clientCryptoPlugin.getWallets());
299
+ console.log("[SERVICE] Using service plugin with wallets:", serviceCryptoPlugin.getWallets());
300
+ // Call the tool WITHOUT providing wallet information in the params
301
+ // The client plugin should automatically add wallet info
302
+ const params = {
303
+ amount: 0.05,
304
+ chain: "sol",
305
+ recipientAddress: "SolRecipient222222222222222222222222222222222"
306
+ // No wallets provided - they should come from the client plugin
307
+ };
308
+ // Call the tool
309
+ const result = await dainConnection.callTool("crypto-transfer", params);
310
+ console.log("[CLIENT] Received response:", JSON.stringify(result, null, 2));
311
+ // Verify the response
312
+ expect(result.data.success).toBe(true);
313
+ expect(result.data.message).toContain("Created unsigned sol transaction");
314
+ // Use the CLIENT plugin helper to extract transaction requests
315
+ const signatureRequests = clientCryptoPlugin.getRequestedCryptoSignatures(result);
316
+ console.log("[CLIENT] Extracted signature requests:", JSON.stringify(signatureRequests, null, 2));
317
+ expect(signatureRequests).toBeDefined();
318
+ expect(signatureRequests.length).toBe(1);
319
+ expect(signatureRequests[0].txs.length).toBe(1);
320
+ const transaction = signatureRequests[0].txs[0];
321
+ expect(transaction.chain).toBe("sol");
322
+ expect(transaction.signer).toBe(testWallets[0].address);
323
+ expect(transaction.encodedTx).toBeDefined();
324
+ // Verify the transaction has an ID
325
+ expect(transaction.id).toBeDefined();
326
+ expect(typeof transaction.id).toBe("string");
327
+ expect(transaction.id.length).toBeGreaterThan(10);
328
+ // Save the transaction ID for later use
329
+ const txId = transaction.id;
330
+ console.log(`[CLIENT] Transaction ID: ${txId}`);
331
+ // Test getTransactionById method
332
+ const foundTx = clientCryptoPlugin.getTransactionById(result, txId);
333
+ expect(foundTx).toBeDefined();
334
+ expect(foundTx?.id).toBe(txId);
335
+ });
336
+ it("should handle Ethereum transactions with unique IDs", async () => {
337
+ // Prepare the tool parameters without wallet information
338
+ const params = {
339
+ amount: 0.25,
340
+ chain: "eth",
341
+ recipientAddress: "0xRecipientEthereumAddress0123456789abcdef"
342
+ // No wallets provided - they should come from the client plugin
343
+ };
344
+ // Call the tool
345
+ const result = await dainConnection.callTool("crypto-transfer", params);
346
+ // Verify the response
347
+ expect(result.data.success).toBe(true);
348
+ expect(result.data.message).toContain("Created unsigned eth transaction");
349
+ // Get transaction requests using the CLIENT plugin
350
+ const signatureRequests = clientCryptoPlugin.getRequestedCryptoSignatures(result);
351
+ const transaction = signatureRequests[0].txs[0];
352
+ expect(transaction.chain).toBe("eth");
353
+ expect(transaction.signer).toBe(testWallets[1].address);
354
+ expect(transaction.metadata?.amount).toBe(0.25);
355
+ expect(transaction.metadata?.recipient).toBe("0xRecipientEthereumAddress0123456789abcdef");
356
+ // Verify the transaction has an ID
357
+ expect(transaction.id).toBeDefined();
358
+ expect(typeof transaction.id).toBe("string");
359
+ console.log(`[CLIENT] Ethereum transaction ID: ${transaction.id}`);
360
+ });
361
+ it("should show wallet information flow and transaction IDs", async () => {
362
+ // Add a new custom wallet to the client plugin
363
+ clientCryptoPlugin.addWallet({
364
+ chain: "sol",
365
+ address: "CustomSolAddress123456789"
366
+ });
367
+ // Call the tool with the updated client plugin
368
+ const result = await dainConnection.callTool("crypto-transfer", {
369
+ amount: 0.75,
370
+ chain: "sol",
371
+ recipientAddress: "NewRecipientAddress"
372
+ });
373
+ // Verify the result uses the updated wallet
374
+ const signatureRequests = clientCryptoPlugin.getRequestedCryptoSignatures(result);
375
+ const transaction = signatureRequests[0].txs[0];
376
+ // The address should be the new custom one we just added
377
+ expect(transaction.signer).toBe("CustomSolAddress123456789");
378
+ expect(transaction.metadata?.amount).toBe(0.75);
379
+ // Verify the transaction has an ID
380
+ expect(transaction.id).toBeDefined();
381
+ console.log(`[CLIENT] Updated wallet transaction ID: ${transaction.id}`);
382
+ });
383
+ it("should demonstrate creating and accessing transactions with IDs", async () => {
384
+ // Create a transaction directly with the createTransaction method
385
+ const txData = {
386
+ encodedTx: "0xManuallyCreatedTx",
387
+ chain: "eth",
388
+ signer: "0xManualAddress",
389
+ metadata: { test: true, purpose: "Demo" }
390
+ };
391
+ // Create a transaction with the client plugin
392
+ const tx1 = clientCryptoPlugin.createTransaction(txData);
393
+ // Create another transaction with the same data
394
+ const tx2 = clientCryptoPlugin.createTransaction(txData);
395
+ // Create a transaction with a predefined ID
396
+ const customId = (0, cuid2_1.createId)();
397
+ const tx3 = clientCryptoPlugin.createTransaction({
398
+ ...txData,
399
+ id: customId
400
+ });
401
+ // Verify each transaction has a unique ID
402
+ expect(tx1.id).toBeDefined();
403
+ expect(tx2.id).toBeDefined();
404
+ expect(tx3.id).toBeDefined();
405
+ // Transaction IDs should be unique
406
+ expect(tx1.id).not.toBe(tx2.id);
407
+ // Custom ID should be preserved
408
+ expect(tx3.id).toBe(customId);
409
+ console.log(`[TEST] Manual transaction IDs:`, {
410
+ tx1Id: tx1.id,
411
+ tx2Id: tx2.id,
412
+ tx3Id: tx3.id
413
+ });
414
+ // Create a signature request with multiple transactions
415
+ const signatureRequest = clientCryptoPlugin.createTransactionRequest([tx1, tx2, tx3]);
416
+ // Verify the signature request contains all transactions
417
+ expect(signatureRequest.txs.length).toBe(3);
418
+ expect(signatureRequest.txs.map(tx => tx.id)).toContain(tx1.id);
419
+ expect(signatureRequest.txs.map(tx => tx.id)).toContain(tx2.id);
420
+ expect(signatureRequest.txs.map(tx => tx.id)).toContain(tx3.id);
421
+ });
422
+ it("should demonstrate new transaction option parameters", async () => {
423
+ // Create transaction data
424
+ const txData = {
425
+ encodedTx: "0xTestTransactionForOptions",
426
+ chain: "eth",
427
+ signer: "0xTestAddress",
428
+ metadata: { test: true, purpose: "Testing Options" }
429
+ };
430
+ // Create transactions
431
+ const tx1 = clientCryptoPlugin.createTransaction(txData);
432
+ const tx2 = clientCryptoPlugin.createTransaction({
433
+ ...txData,
434
+ signer: "0xAnotherTestAddress"
435
+ });
436
+ // Create a transaction request with options
437
+ const signatureRequest = clientCryptoPlugin.createTransactionRequest([tx1, tx2], {
438
+ executeSimultaneously: true,
439
+ toolsShouldWaitForConfirmation: true
440
+ });
441
+ // Verify the request has the correct options
442
+ expect(signatureRequest.executeSimultaneously).toBe(true);
443
+ expect(signatureRequest.toolsShouldWaitForConfirmation).toBe(true);
444
+ expect(signatureRequest.txs.length).toBe(2);
445
+ // Test calling the tool with options
446
+ const result = await dainConnection.callTool("crypto-transfer", {
447
+ amount: 1.5,
448
+ chain: "eth",
449
+ recipientAddress: "0xOptionTestRecipientAddress",
450
+ executeSimultaneously: true,
451
+ waitForConfirmation: true
452
+ });
453
+ // Extract the signature requests
454
+ const requests = clientCryptoPlugin.getRequestedCryptoSignatures(result);
455
+ // Verify the options were passed through
456
+ expect(requests.length).toBe(1);
457
+ expect(requests[0].executeSimultaneously).toBe(true);
458
+ expect(requests[0].toolsShouldWaitForConfirmation).toBe(true);
459
+ // Verify the transaction was created correctly
460
+ expect(requests[0].txs.length).toBe(1);
461
+ expect(requests[0].txs[0].chain).toBe("eth");
462
+ expect(requests[0].txs[0].metadata?.amount).toBe(1.5);
463
+ expect(requests[0].txs[0].metadata?.recipient).toBe("0xOptionTestRecipientAddress");
464
+ });
465
+ it("should preserve UI components in transactions and signature requests", async () => {
466
+ console.log("[TEST] Testing UI components in crypto transactions");
467
+ // Call the tool to create a Solana transaction
468
+ const solResult = await dainConnection.callTool("crypto-transfer", {
469
+ amount: 2.5,
470
+ chain: "sol",
471
+ recipientAddress: "SolUITestRecipient111111111111111111111111111"
472
+ });
473
+ // Extract the signature requests
474
+ const solRequests = clientCryptoPlugin.getRequestedCryptoSignatures(solResult);
475
+ // Verify the transaction has UI components
476
+ expect(solRequests[0].txs[0].ui).toBeDefined();
477
+ expect(solRequests[0].txs[0].ui.type).toBe("transaction");
478
+ expect(solRequests[0].txs[0].ui.title).toBe("Solana Transfer");
479
+ expect(solRequests[0].txs[0].ui.icon).toBe("💸");
480
+ expect(solRequests[0].txs[0].ui.theme).toBe("purple");
481
+ expect(solRequests[0].txs[0].ui.fields).toBeDefined();
482
+ expect(solRequests[0].txs[0].ui.fields.length).toBe(3);
483
+ // Verify the signature request has UI components
484
+ expect(solRequests[0].ui).toBeDefined();
485
+ expect(solRequests[0].ui.type).toBe("transaction-group");
486
+ expect(solRequests[0].ui.title).toBe("Transaction Approval Required");
487
+ expect(solRequests[0].ui.actions).toBeDefined();
488
+ expect(solRequests[0].ui.actions.length).toBe(2);
489
+ console.log("[TEST] Solana transaction UI:", JSON.stringify(solRequests[0].txs[0].ui, null, 2));
490
+ console.log("[TEST] Signature request UI:", JSON.stringify(solRequests[0].ui, null, 2));
491
+ // Test Ethereum transaction UI
492
+ const ethResult = await dainConnection.callTool("crypto-transfer", {
493
+ amount: 0.1,
494
+ chain: "eth",
495
+ recipientAddress: "0xEthUITestRecipient0123456789abcdef"
496
+ });
497
+ const ethRequests = clientCryptoPlugin.getRequestedCryptoSignatures(ethResult);
498
+ // Verify Ethereum transaction UI
499
+ expect(ethRequests[0].txs[0].ui).toBeDefined();
500
+ expect(ethRequests[0].txs[0].ui.type).toBe("transaction");
501
+ expect(ethRequests[0].txs[0].ui.title).toBe("Ethereum Transfer");
502
+ expect(ethRequests[0].txs[0].ui.icon).toBe("Ξ");
503
+ expect(ethRequests[0].txs[0].ui.theme).toBe("blue");
504
+ expect(ethRequests[0].txs[0].ui.fields.length).toBe(4); // ETH has gas limit field
505
+ // Test Arbitrum transaction UI
506
+ const arbResult = await dainConnection.callTool("crypto-transfer", {
507
+ amount: 5.0,
508
+ chain: "arb",
509
+ recipientAddress: "0xArbUITestRecipient0123456789abcdef"
510
+ });
511
+ const arbRequests = clientCryptoPlugin.getRequestedCryptoSignatures(arbResult);
512
+ // Verify Arbitrum transaction UI
513
+ expect(arbRequests[0].txs[0].ui).toBeDefined();
514
+ expect(arbRequests[0].txs[0].ui.type).toBe("transaction");
515
+ expect(arbRequests[0].txs[0].ui.title).toBe("Arbitrum Transfer");
516
+ expect(arbRequests[0].txs[0].ui.icon).toBe("🔷");
517
+ expect(arbRequests[0].txs[0].ui.theme).toBe("orange");
518
+ expect(arbRequests[0].txs[0].ui.fields[2].badge).toBe("L2");
519
+ console.log("[TEST] All UI components preserved successfully");
520
+ });
521
+ it("should handle transactions with custom UI components", async () => {
522
+ // Create a transaction with custom UI using the plugin helper
523
+ const customUITransaction = clientCryptoPlugin.createTransaction({
524
+ encodedTx: "0xCustomUITransaction",
525
+ chain: "eth",
526
+ signer: "0xCustomUIWallet",
527
+ metadata: { customField: "Custom Value" },
528
+ ui: {
529
+ type: "custom-transaction",
530
+ title: "Custom Transaction UI",
531
+ customProperty: "This is a custom UI component",
532
+ fields: [
533
+ { label: "Custom Field", value: "Custom Value" }
534
+ ]
535
+ }
536
+ });
537
+ // Verify the custom UI is preserved
538
+ expect(customUITransaction.ui).toBeDefined();
539
+ expect(customUITransaction.ui.type).toBe("custom-transaction");
540
+ expect(customUITransaction.ui.customProperty).toBe("This is a custom UI component");
541
+ // Create a signature request with custom UI
542
+ const customRequest = clientCryptoPlugin.createTransactionRequest([customUITransaction], {
543
+ executeSimultaneously: false,
544
+ toolsShouldWaitForConfirmation: true,
545
+ ui: {
546
+ type: "custom-request",
547
+ title: "Custom Request UI",
548
+ customStyles: { backgroundColor: "#f0f0f0" }
549
+ }
550
+ });
551
+ // Verify both transaction and request UI are preserved
552
+ expect(customRequest.txs[0].ui).toBeDefined();
553
+ expect(customRequest.txs[0].ui.type).toBe("custom-transaction");
554
+ expect(customRequest.ui).toBeDefined();
555
+ expect(customRequest.ui.type).toBe("custom-request");
556
+ expect(customRequest.ui.customStyles).toEqual({ backgroundColor: "#f0f0f0" });
557
+ console.log("[TEST] Custom UI preserved:", {
558
+ transactionUI: customUITransaction.ui,
559
+ requestUI: customRequest.ui
560
+ });
561
+ });
562
+ it("should verify plugins provide wallet data to context handlers on service side", async () => {
563
+ // Create a mock context for testing
564
+ const mockContext = {
565
+ context: {
566
+ plugins: {
567
+ 'crypto-plugin': {
568
+ wallets: [
569
+ { chain: "sol", address: "CustomSolAddress123456789" },
570
+ { chain: "eth", address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }
571
+ ]
572
+ }
573
+ }
574
+ }
575
+ };
576
+ // Use the helper method to get wallets
577
+ const wallets = serviceCryptoPlugin.getUserWallets(mockContext.context);
578
+ expect(wallets).toBeDefined();
579
+ expect(wallets.length).toBe(2);
580
+ // Test the chain-specific helper method
581
+ const solWallet = serviceCryptoPlugin.getUserWalletForChain(mockContext.context, "sol");
582
+ expect(solWallet).toBe("CustomSolAddress123456789");
583
+ const ethWallet = serviceCryptoPlugin.getUserWalletForChain(mockContext.context, "eth");
584
+ expect(ethWallet).toBe("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
585
+ // Directly call the context handler function
586
+ const contextData = await walletContext.getContextData({}, mockContext.context);
587
+ // Verify wallet information was properly extracted
588
+ expect(contextData.connectedWallets).toBeDefined();
589
+ expect(contextData.connectedWallets.length).toBe(2);
590
+ expect(contextData.walletsCount).toBe(2);
591
+ // Verify the wallet information matches our test wallets
592
+ const wallet = contextData.connectedWallets.find(w => w.chain === "sol");
593
+ expect(wallet).toBeDefined();
594
+ expect(wallet?.address).toBe("CustomSolAddress123456789");
595
+ });
596
+ it("should verify plugins provide wallet data to datasource handlers on service side", async () => {
597
+ // Create a mock context for testing
598
+ const mockContext = {
599
+ plugins: {
600
+ 'crypto-plugin': {
601
+ wallets: [
602
+ { chain: "sol", address: "CustomSolAddress123456789" },
603
+ { chain: "eth", address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" },
604
+ { chain: "arb", address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" }
605
+ ]
606
+ }
607
+ }
608
+ };
609
+ // Use the helper method to get wallets
610
+ const wallets = serviceCryptoPlugin.getUserWallets({ plugins: mockContext.plugins });
611
+ expect(wallets).toBeDefined();
612
+ expect(wallets.length).toBe(3);
613
+ // Test the chain-specific helper method
614
+ const solWallet = serviceCryptoPlugin.getUserWalletForChain({ plugins: mockContext.plugins }, "sol");
615
+ expect(solWallet).toBe("CustomSolAddress123456789");
616
+ const ethWallet = serviceCryptoPlugin.getUserWalletForChain({ plugins: mockContext.plugins }, "eth");
617
+ expect(ethWallet).toBe("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
618
+ const arbWallet = serviceCryptoPlugin.getUserWalletForChain({ plugins: mockContext.plugins }, "arb");
619
+ expect(arbWallet).toBe("0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
620
+ // Test with chain filter
621
+ const ethBalanceData = await walletBalanceDatasource.getDatasource({}, { chain: "eth" }, { plugins: mockContext.plugins });
622
+ // Verify balance information
623
+ expect(ethBalanceData.balances).toBeDefined();
624
+ expect(ethBalanceData.balances.length).toBe(1); // Only ETH wallet
625
+ expect(ethBalanceData.balances[0].chain).toBe("eth");
626
+ expect(ethBalanceData.balances[0].token).toBe("ETH");
627
+ // Test without filter
628
+ const allBalancesData = await walletBalanceDatasource.getDatasource({}, {}, { plugins: mockContext.plugins });
629
+ // Verify we get balances for all wallets
630
+ expect(allBalancesData.balances.length).toBe(3); // SOL, ETH, ARB wallets
631
+ // Verify the wallet chains
632
+ const chains = allBalancesData.balances.map(b => b.chain);
633
+ expect(chains).toContain("sol");
634
+ expect(chains).toContain("eth");
635
+ expect(chains).toContain("arb");
636
+ });
637
+ it("should allow service plugin to process transactions with additional data", async () => {
638
+ // Create a new test with a local function that adds metadata
639
+ // We'll use a real function instead of adding to the plugin instance
640
+ const addServiceMetadata = (tx) => {
641
+ return {
642
+ ...tx,
643
+ metadata: {
644
+ ...tx.metadata,
645
+ timestamp: Date.now(),
646
+ serviceId: "test-service-id",
647
+ enhanced: true
648
+ }
649
+ };
650
+ };
651
+ // Create a simple tool handler that would use this function
652
+ // This simulates what a service would do
653
+ const processTransaction = async (chain, userWallet, options) => {
654
+ // Create a basic transaction with ID
655
+ const baseTx = {
656
+ encodedTx: "0xBasicTransaction",
657
+ chain: chain,
658
+ signer: userWallet,
659
+ metadata: { basic: true }
660
+ };
661
+ // Create transaction with ID using the helper
662
+ const tx = serviceCryptoPlugin.createTransaction(baseTx);
663
+ // Enhance it with service-side metadata
664
+ const enhancedTx = addServiceMetadata(tx);
665
+ // Create the signature request
666
+ const request = serviceCryptoPlugin.createTransactionRequest([enhancedTx], options);
667
+ return {
668
+ success: true,
669
+ signatureRequests: [request]
670
+ };
671
+ };
672
+ // Simulate processing with options
673
+ const testWallet = "0xTestWalletAddress";
674
+ const result = await processTransaction("eth", testWallet, {
675
+ executeSimultaneously: true,
676
+ toolsShouldWaitForConfirmation: false
677
+ });
678
+ // Verify the metadata was added
679
+ expect(result.signatureRequests[0].txs[0].metadata?.enhanced).toBe(true);
680
+ expect(result.signatureRequests[0].txs[0].metadata?.serviceId).toBe("test-service-id");
681
+ expect(result.signatureRequests[0].txs[0].metadata?.timestamp).toBeDefined();
682
+ expect(result.signatureRequests[0].txs[0].metadata?.basic).toBe(true); // Original data preserved
683
+ // Verify the transaction options
684
+ expect(result.signatureRequests[0].executeSimultaneously).toBe(true);
685
+ expect(result.signatureRequests[0].toolsShouldWaitForConfirmation).toBe(false);
686
+ // Make sure the transaction info is correct
687
+ expect(result.signatureRequests[0].txs[0].chain).toBe("eth");
688
+ expect(result.signatureRequests[0].txs[0].signer).toBe(testWallet);
689
+ // Verify the transaction has an ID
690
+ expect(result.signatureRequests[0].txs[0].id).toBeDefined();
691
+ console.log(`[TEST] Service processed transaction ID: ${result.signatureRequests[0].txs[0].id}`);
692
+ });
693
+ });
694
+ //# sourceMappingURL=crypto-plugin.test.js.map