@miden-sdk/miden-wallet-adapter-miden 0.16.0 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +93 -0
- package/dist/adapter.js.map +1 -1
- package/dist/conformance.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +9 -4
- package/__tests__/adapter.test.ts +0 -594
- package/__tests__/conformance.test.ts +0 -131
- package/adapter.ts +0 -449
- package/conformance.ts +0 -219
- package/dist/__tests__/adapter.test.d.ts +0 -1
- package/dist/__tests__/adapter.test.js +0 -464
- package/dist/__tests__/adapter.test.js.map +0 -1
- package/dist/__tests__/conformance.test.d.ts +0 -1
- package/dist/__tests__/conformance.test.js +0 -97
- package/dist/__tests__/conformance.test.js.map +0 -1
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -8
- package/dist/vitest.config.js.map +0 -1
- package/docs/README.md +0 -20
- package/docs/classes/MidenWalletAdapter.md +0 -714
- package/docs/interfaces/MidenWallet.md +0 -506
- package/docs/interfaces/MidenWalletAdapterConfig.md +0 -13
- package/docs/interfaces/MidenWalletEvents.md +0 -55
- package/docs/interfaces/MidenWindow.md +0 -5217
- package/docs/variables/MidenWalletName.md +0 -9
- package/index.ts +0 -2
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -8
|
@@ -1,594 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
WalletNotConnectedError,
|
|
4
|
-
WalletTransactionError,
|
|
5
|
-
WalletReadyState,
|
|
6
|
-
WalletNotReadyError,
|
|
7
|
-
WalletConnectionError,
|
|
8
|
-
PrivateDataPermission,
|
|
9
|
-
WalletAdapterNetwork,
|
|
10
|
-
} from "@miden-sdk/miden-wallet-adapter-base";
|
|
11
|
-
|
|
12
|
-
// Mock @miden-sdk/miden-sdk before importing adapter
|
|
13
|
-
vi.mock("@miden-sdk/miden-sdk", () => ({
|
|
14
|
-
Note: {
|
|
15
|
-
deserialize: vi.fn((bytes: Uint8Array) => ({ mockNote: true, bytes })),
|
|
16
|
-
},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
import { Note } from "@miden-sdk/miden-sdk";
|
|
20
|
-
import { MidenWalletAdapter } from "../adapter";
|
|
21
|
-
|
|
22
|
-
function createMockWallet(overrides: Record<string, any> = {}) {
|
|
23
|
-
return {
|
|
24
|
-
address: "0xmockaddress",
|
|
25
|
-
publicKey: new Uint8Array([1, 2, 3]),
|
|
26
|
-
connect: vi.fn(),
|
|
27
|
-
disconnect: vi.fn(),
|
|
28
|
-
requestSend: vi.fn().mockResolvedValue({ transactionId: "tx-send-1" }),
|
|
29
|
-
requestConsume: vi
|
|
30
|
-
.fn()
|
|
31
|
-
.mockResolvedValue({ transactionId: "tx-consume-1" }),
|
|
32
|
-
requestTransaction: vi
|
|
33
|
-
.fn()
|
|
34
|
-
.mockResolvedValue({ transactionId: "tx-custom-1" }),
|
|
35
|
-
requestAssets: vi
|
|
36
|
-
.fn()
|
|
37
|
-
.mockResolvedValue({ assets: [{ faucetId: "f1", amount: "100" }] }),
|
|
38
|
-
requestGuardianInfo: vi.fn().mockResolvedValue({
|
|
39
|
-
guardianInfo: {
|
|
40
|
-
isGuardianAccount: false,
|
|
41
|
-
guardianEndpoint: null,
|
|
42
|
-
guardianProvider: null,
|
|
43
|
-
guardianSyncStatus: null,
|
|
44
|
-
},
|
|
45
|
-
}),
|
|
46
|
-
requestPrivateNotes: vi
|
|
47
|
-
.fn()
|
|
48
|
-
.mockResolvedValue({ privateNotes: [{ noteId: "n1" }] }),
|
|
49
|
-
signBytes: vi
|
|
50
|
-
.fn()
|
|
51
|
-
.mockResolvedValue({ signature: new Uint8Array([9, 8, 7]) }),
|
|
52
|
-
importPrivateNote: vi.fn().mockResolvedValue({ noteId: "imported-note-1" }),
|
|
53
|
-
requestConsumableNotes: vi
|
|
54
|
-
.fn()
|
|
55
|
-
.mockResolvedValue({ consumableNotes: [{ noteId: "cn1" }] }),
|
|
56
|
-
createAccount: vi.fn().mockResolvedValue({ accountId: "account-123" }),
|
|
57
|
-
waitForTransaction: vi.fn(),
|
|
58
|
-
...overrides,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Helper to set up a connected adapter by injecting mock wallet via window
|
|
63
|
-
async function createConnectedAdapter() {
|
|
64
|
-
const mockWallet = createMockWallet();
|
|
65
|
-
(window as any).midenWallet = mockWallet;
|
|
66
|
-
|
|
67
|
-
const adapter = new MidenWalletAdapter();
|
|
68
|
-
// Force readyState to Installed so connect works
|
|
69
|
-
adapter.readyState = WalletReadyState.Installed;
|
|
70
|
-
|
|
71
|
-
await adapter.connect(
|
|
72
|
-
PrivateDataPermission.UponRequest,
|
|
73
|
-
WalletAdapterNetwork.Testnet
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
return { adapter, mockWallet };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
describe("MidenWalletAdapter", () => {
|
|
80
|
-
beforeEach(() => {
|
|
81
|
-
vi.clearAllMocks();
|
|
82
|
-
delete (window as any).midenWallet;
|
|
83
|
-
delete (window as any).miden;
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe("constructor", () => {
|
|
87
|
-
it("initializes with null address and not connecting", () => {
|
|
88
|
-
const adapter = new MidenWalletAdapter();
|
|
89
|
-
expect(adapter.address).toBeNull();
|
|
90
|
-
expect(adapter.publicKey).toBeNull();
|
|
91
|
-
expect(adapter.connecting).toBe(false);
|
|
92
|
-
expect(adapter.connected).toBe(false);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("sets readyState to NotDetected when wallet is not present", () => {
|
|
96
|
-
const adapter = new MidenWalletAdapter();
|
|
97
|
-
expect(adapter.readyState).toBe(WalletReadyState.NotDetected);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe("not connected errors", () => {
|
|
102
|
-
const methods: Array<{
|
|
103
|
-
name: string;
|
|
104
|
-
call: (a: MidenWalletAdapter) => Promise<any>;
|
|
105
|
-
}> = [
|
|
106
|
-
{
|
|
107
|
-
name: "requestSend",
|
|
108
|
-
call: (a) =>
|
|
109
|
-
a.requestSend({
|
|
110
|
-
senderAddress: "s",
|
|
111
|
-
recipientAddress: "r",
|
|
112
|
-
faucetId: "f",
|
|
113
|
-
noteType: "public",
|
|
114
|
-
amount: 1,
|
|
115
|
-
}),
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: "requestConsume",
|
|
119
|
-
call: (a) =>
|
|
120
|
-
a.requestConsume({
|
|
121
|
-
faucetId: "f",
|
|
122
|
-
noteId: "n",
|
|
123
|
-
noteType: "public",
|
|
124
|
-
amount: 1,
|
|
125
|
-
}),
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: "requestTransaction",
|
|
129
|
-
call: (a) =>
|
|
130
|
-
a.requestTransaction({ type: "send" as any, payload: {} as any }),
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
name: "requestAssets",
|
|
134
|
-
call: (a) => a.requestAssets(),
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
name: "requestGuardianInfo",
|
|
138
|
-
call: (a) => a.requestGuardianInfo(),
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
name: "requestPrivateNotes",
|
|
142
|
-
call: (a) => a.requestPrivateNotes("All" as any),
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
name: "signBytes",
|
|
146
|
-
call: (a) => a.signBytes(new Uint8Array([1]), "word"),
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
name: "importPrivateNote",
|
|
150
|
-
call: (a) => a.importPrivateNote(new Uint8Array([1])),
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
name: "requestConsumableNotes",
|
|
154
|
-
call: (a) => a.requestConsumableNotes(),
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
name: "waitForTransaction",
|
|
158
|
-
call: (a) => a.waitForTransaction("tx-1"),
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
name: "createAccount",
|
|
162
|
-
call: (a) => a.createAccount(),
|
|
163
|
-
},
|
|
164
|
-
];
|
|
165
|
-
|
|
166
|
-
for (const { name, call } of methods) {
|
|
167
|
-
it(`${name} throws WalletNotConnectedError when not connected`, async () => {
|
|
168
|
-
const adapter = new MidenWalletAdapter();
|
|
169
|
-
const errorHandler = vi.fn();
|
|
170
|
-
adapter.on("error", errorHandler);
|
|
171
|
-
|
|
172
|
-
await expect(call(adapter)).rejects.toThrow(WalletNotConnectedError);
|
|
173
|
-
expect(errorHandler).toHaveBeenCalledWith(
|
|
174
|
-
expect.any(WalletNotConnectedError)
|
|
175
|
-
);
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
describe("connected operations", () => {
|
|
181
|
-
it("requestSend delegates to wallet and returns transactionId", async () => {
|
|
182
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
183
|
-
const tx = {
|
|
184
|
-
senderAddress: "s",
|
|
185
|
-
recipientAddress: "r",
|
|
186
|
-
faucetId: "f",
|
|
187
|
-
noteType: "public" as const,
|
|
188
|
-
amount: 100,
|
|
189
|
-
};
|
|
190
|
-
const result = await adapter.requestSend(tx);
|
|
191
|
-
expect(mockWallet.requestSend).toHaveBeenCalledWith(tx);
|
|
192
|
-
expect(result).toBe("tx-send-1");
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("requestConsume delegates to wallet and returns transactionId", async () => {
|
|
196
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
197
|
-
const tx = {
|
|
198
|
-
faucetId: "f",
|
|
199
|
-
noteId: "n",
|
|
200
|
-
noteType: "public" as const,
|
|
201
|
-
amount: 50,
|
|
202
|
-
};
|
|
203
|
-
const result = await adapter.requestConsume(tx);
|
|
204
|
-
expect(mockWallet.requestConsume).toHaveBeenCalledWith(tx);
|
|
205
|
-
expect(result).toBe("tx-consume-1");
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it("requestSend rejects (does not resolve undefined) when the wallet returns no transactionId (0xMiden/wallet#314)", async () => {
|
|
209
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
210
|
-
// The wallet resolved "successfully" but without a transaction id — meaning
|
|
211
|
-
// the transaction was never submitted; the dApp must see a rejection.
|
|
212
|
-
mockWallet.requestSend.mockResolvedValue({});
|
|
213
|
-
const errorHandler = vi.fn();
|
|
214
|
-
adapter.on("error", errorHandler);
|
|
215
|
-
|
|
216
|
-
await expect(
|
|
217
|
-
adapter.requestSend({
|
|
218
|
-
senderAddress: "s",
|
|
219
|
-
recipientAddress: "r",
|
|
220
|
-
faucetId: "f",
|
|
221
|
-
noteType: "public" as const,
|
|
222
|
-
amount: 1,
|
|
223
|
-
})
|
|
224
|
-
).rejects.toThrow(WalletTransactionError);
|
|
225
|
-
expect(errorHandler).toHaveBeenCalled();
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it("requestConsume rejects when the wallet returns no transactionId (0xMiden/wallet#314)", async () => {
|
|
229
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
230
|
-
mockWallet.requestConsume.mockResolvedValue({ transactionId: undefined });
|
|
231
|
-
|
|
232
|
-
await expect(
|
|
233
|
-
adapter.requestConsume({
|
|
234
|
-
faucetId: "f",
|
|
235
|
-
noteId: "n",
|
|
236
|
-
noteType: "public" as const,
|
|
237
|
-
amount: 1,
|
|
238
|
-
})
|
|
239
|
-
).rejects.toThrow(WalletTransactionError);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("requestTransaction (custom) rejects when the wallet returns no transactionId (0xMiden/wallet#314)", async () => {
|
|
243
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
244
|
-
mockWallet.requestTransaction.mockResolvedValue({});
|
|
245
|
-
|
|
246
|
-
await expect(
|
|
247
|
-
adapter.requestTransaction({
|
|
248
|
-
type: "custom" as any,
|
|
249
|
-
payload: {} as any,
|
|
250
|
-
})
|
|
251
|
-
).rejects.toThrow(WalletTransactionError);
|
|
252
|
-
expect(mockWallet.requestTransaction).toHaveBeenCalled();
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it("requestTransaction routes a Consume transaction to wallet.requestConsume (issue #88)", async () => {
|
|
256
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
257
|
-
const payload = {
|
|
258
|
-
faucetId: "f",
|
|
259
|
-
noteId: "n",
|
|
260
|
-
noteType: "public" as const,
|
|
261
|
-
amount: 50,
|
|
262
|
-
};
|
|
263
|
-
const result = await adapter.requestTransaction({
|
|
264
|
-
type: "consume" as any,
|
|
265
|
-
payload,
|
|
266
|
-
});
|
|
267
|
-
expect(mockWallet.requestConsume).toHaveBeenCalledWith(payload);
|
|
268
|
-
expect(mockWallet.requestTransaction).not.toHaveBeenCalled();
|
|
269
|
-
expect(result).toBe("tx-consume-1");
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it("requestTransaction routes a Send transaction to wallet.requestSend", async () => {
|
|
273
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
274
|
-
const payload = {
|
|
275
|
-
senderAddress: "s",
|
|
276
|
-
recipientAddress: "r",
|
|
277
|
-
faucetId: "f",
|
|
278
|
-
noteType: "public" as const,
|
|
279
|
-
amount: 100,
|
|
280
|
-
};
|
|
281
|
-
const result = await adapter.requestTransaction({
|
|
282
|
-
type: "send" as any,
|
|
283
|
-
payload,
|
|
284
|
-
});
|
|
285
|
-
expect(mockWallet.requestSend).toHaveBeenCalledWith(payload);
|
|
286
|
-
expect(mockWallet.requestTransaction).not.toHaveBeenCalled();
|
|
287
|
-
expect(result).toBe("tx-send-1");
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it("requestTransaction forwards a Custom transaction to wallet.requestTransaction", async () => {
|
|
291
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
292
|
-
const tx = {
|
|
293
|
-
type: "custom" as any,
|
|
294
|
-
payload: {
|
|
295
|
-
address: "addr",
|
|
296
|
-
recipientAddress: "r",
|
|
297
|
-
transactionRequest: "base64-request",
|
|
298
|
-
} as any,
|
|
299
|
-
};
|
|
300
|
-
const result = await adapter.requestTransaction(tx);
|
|
301
|
-
expect(mockWallet.requestTransaction).toHaveBeenCalledWith(tx);
|
|
302
|
-
expect(result).toBe("tx-custom-1");
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it("requestTransaction forwards a bare (typeless) payload to wallet.requestTransaction", async () => {
|
|
306
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
307
|
-
const tx = {
|
|
308
|
-
address: "addr",
|
|
309
|
-
transactionRequest: "base64-request",
|
|
310
|
-
} as any;
|
|
311
|
-
const result = await adapter.requestTransaction(tx);
|
|
312
|
-
expect(mockWallet.requestTransaction).toHaveBeenCalledWith(tx);
|
|
313
|
-
expect(result).toBe("tx-custom-1");
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it("requestAssets returns assets array", async () => {
|
|
317
|
-
const { adapter } = await createConnectedAdapter();
|
|
318
|
-
const assets = await adapter.requestAssets();
|
|
319
|
-
expect(assets).toEqual([{ faucetId: "f1", amount: "100" }]);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
it("requestGuardianInfo unwraps the provider result", async () => {
|
|
323
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
324
|
-
mockWallet.requestGuardianInfo.mockResolvedValue({
|
|
325
|
-
guardianInfo: {
|
|
326
|
-
isGuardianAccount: true,
|
|
327
|
-
guardianEndpoint: "https://g",
|
|
328
|
-
guardianProvider: "gateway",
|
|
329
|
-
guardianSyncStatus: "in-sync",
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
const guardianInfo = await adapter.requestGuardianInfo();
|
|
334
|
-
expect(guardianInfo).toEqual({
|
|
335
|
-
isGuardianAccount: true,
|
|
336
|
-
guardianEndpoint: "https://g",
|
|
337
|
-
guardianProvider: "gateway",
|
|
338
|
-
guardianSyncStatus: "in-sync",
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it("requestGuardianInfo emits error and wraps failure", async () => {
|
|
343
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
344
|
-
mockWallet.requestGuardianInfo.mockRejectedValue(
|
|
345
|
-
new Error("network error")
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
const errorHandler = vi.fn();
|
|
349
|
-
adapter.on("error", errorHandler);
|
|
350
|
-
|
|
351
|
-
await expect(adapter.requestGuardianInfo()).rejects.toThrow(
|
|
352
|
-
WalletTransactionError
|
|
353
|
-
);
|
|
354
|
-
expect(errorHandler).toHaveBeenCalledWith(
|
|
355
|
-
expect.any(WalletTransactionError)
|
|
356
|
-
);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
it("requestPrivateNotes returns notes array", async () => {
|
|
360
|
-
const { adapter } = await createConnectedAdapter();
|
|
361
|
-
const notes = await adapter.requestPrivateNotes("All" as any);
|
|
362
|
-
expect(notes).toEqual([{ noteId: "n1" }]);
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
it("signBytes returns signature", async () => {
|
|
366
|
-
const { adapter } = await createConnectedAdapter();
|
|
367
|
-
const sig = await adapter.signBytes(new Uint8Array([1, 2]), "word");
|
|
368
|
-
expect(sig).toEqual(new Uint8Array([9, 8, 7]));
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it("importPrivateNote returns noteId", async () => {
|
|
372
|
-
const { adapter } = await createConnectedAdapter();
|
|
373
|
-
const noteId = await adapter.importPrivateNote(new Uint8Array([1]));
|
|
374
|
-
expect(noteId).toBe("imported-note-1");
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it("requestConsumableNotes returns consumable notes", async () => {
|
|
378
|
-
const { adapter } = await createConnectedAdapter();
|
|
379
|
-
const notes = await adapter.requestConsumableNotes();
|
|
380
|
-
expect(notes).toEqual([{ noteId: "cn1" }]);
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
it("createAccount returns accountId without params", async () => {
|
|
384
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
385
|
-
const result = await adapter.createAccount();
|
|
386
|
-
expect(mockWallet.createAccount).toHaveBeenCalledWith(undefined);
|
|
387
|
-
expect(result).toBe("account-123");
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
it("createAccount returns accountId with params", async () => {
|
|
391
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
392
|
-
const params = {
|
|
393
|
-
accountType: "RegularAccountUpdatableCode" as const,
|
|
394
|
-
storageMode: "private" as const,
|
|
395
|
-
};
|
|
396
|
-
const result = await adapter.createAccount(params);
|
|
397
|
-
expect(mockWallet.createAccount).toHaveBeenCalledWith(params);
|
|
398
|
-
expect(result).toBe("account-123");
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it("createAccount passes customComponents to wallet", async () => {
|
|
402
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
403
|
-
const component = new Uint8Array([1, 2, 3]);
|
|
404
|
-
const params = { customComponents: [component] };
|
|
405
|
-
const result = await adapter.createAccount(params);
|
|
406
|
-
expect(mockWallet.createAccount).toHaveBeenCalledWith(params);
|
|
407
|
-
expect(result).toBe("account-123");
|
|
408
|
-
});
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
describe("waitForTransaction", () => {
|
|
412
|
-
it("success path: deserializes notes and returns TransactionOutput", async () => {
|
|
413
|
-
// base64 of [1,2,3] = "AQID"
|
|
414
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
415
|
-
mockWallet.waitForTransaction.mockResolvedValue({
|
|
416
|
-
txHash: "hash-123",
|
|
417
|
-
outputNotes: ["AQID", "BAUG"],
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
const result = await adapter.waitForTransaction("tx-1");
|
|
421
|
-
|
|
422
|
-
expect(result.txHash).toBe("hash-123");
|
|
423
|
-
expect(result.outputNotes).toHaveLength(2);
|
|
424
|
-
expect(Note.deserialize).toHaveBeenCalledTimes(2);
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
it("error path: throws WalletTransactionError on errorMessage", async () => {
|
|
428
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
429
|
-
mockWallet.waitForTransaction.mockResolvedValue({
|
|
430
|
-
errorMessage: "tx failed",
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
const errorHandler = vi.fn();
|
|
434
|
-
adapter.on("error", errorHandler);
|
|
435
|
-
|
|
436
|
-
await expect(adapter.waitForTransaction("tx-1")).rejects.toThrow(
|
|
437
|
-
WalletTransactionError
|
|
438
|
-
);
|
|
439
|
-
expect(errorHandler).toHaveBeenCalled();
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
describe("connect", () => {
|
|
444
|
-
it("throws WalletNotReadyError if readyState is not Installed", async () => {
|
|
445
|
-
const adapter = new MidenWalletAdapter();
|
|
446
|
-
const errorHandler = vi.fn();
|
|
447
|
-
adapter.on("error", errorHandler);
|
|
448
|
-
|
|
449
|
-
await expect(
|
|
450
|
-
adapter.connect(
|
|
451
|
-
PrivateDataPermission.UponRequest,
|
|
452
|
-
WalletAdapterNetwork.Testnet
|
|
453
|
-
)
|
|
454
|
-
).rejects.toThrow(WalletNotReadyError);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
it("connects successfully and emits connect event", async () => {
|
|
458
|
-
const mockWallet = createMockWallet();
|
|
459
|
-
(window as any).midenWallet = mockWallet;
|
|
460
|
-
|
|
461
|
-
const adapter = new MidenWalletAdapter();
|
|
462
|
-
adapter.readyState = WalletReadyState.Installed;
|
|
463
|
-
|
|
464
|
-
const connectHandler = vi.fn();
|
|
465
|
-
adapter.on("connect", connectHandler);
|
|
466
|
-
|
|
467
|
-
await adapter.connect(
|
|
468
|
-
PrivateDataPermission.UponRequest,
|
|
469
|
-
WalletAdapterNetwork.Testnet
|
|
470
|
-
);
|
|
471
|
-
|
|
472
|
-
expect(adapter.address).toBe("0xmockaddress");
|
|
473
|
-
expect(adapter.publicKey).toEqual(new Uint8Array([1, 2, 3]));
|
|
474
|
-
expect(adapter.connected).toBe(true);
|
|
475
|
-
expect(adapter.connecting).toBe(false);
|
|
476
|
-
expect(connectHandler).toHaveBeenCalledWith("0xmockaddress");
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
it("throws WalletConnectionError if wallet has no address after connect", async () => {
|
|
480
|
-
const mockWallet = createMockWallet({ address: undefined });
|
|
481
|
-
(window as any).midenWallet = mockWallet;
|
|
482
|
-
|
|
483
|
-
const adapter = new MidenWalletAdapter();
|
|
484
|
-
adapter.readyState = WalletReadyState.Installed;
|
|
485
|
-
|
|
486
|
-
await expect(
|
|
487
|
-
adapter.connect(
|
|
488
|
-
PrivateDataPermission.UponRequest,
|
|
489
|
-
WalletAdapterNetwork.Testnet
|
|
490
|
-
)
|
|
491
|
-
).rejects.toThrow(WalletConnectionError);
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
it("re-drives the handshake and re-emits `connect` when called while already connected, repopulating the current account (0xMiden/wallet#470)", async () => {
|
|
495
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
496
|
-
|
|
497
|
-
const connectHandler = vi.fn();
|
|
498
|
-
adapter.on("connect", connectHandler);
|
|
499
|
-
|
|
500
|
-
// The in-app browser preserves this adapter's JS context across a
|
|
501
|
-
// park/restore, so a re-opened dApp finds `connected === true`. The
|
|
502
|
-
// old early-return made a second connect() a silent no-op, so a
|
|
503
|
-
// re-mounted consumer that re-subscribed to events never got its
|
|
504
|
-
// account back ("Connect Wallet works only once"). A repeat connect()
|
|
505
|
-
// must re-drive the wallet handshake — which returns the current
|
|
506
|
-
// account — and re-emit `connect` so the consumer repopulates.
|
|
507
|
-
await adapter.connect(
|
|
508
|
-
PrivateDataPermission.UponRequest,
|
|
509
|
-
WalletAdapterNetwork.Testnet
|
|
510
|
-
);
|
|
511
|
-
|
|
512
|
-
expect(mockWallet.connect).toHaveBeenCalledTimes(2);
|
|
513
|
-
expect(connectHandler).toHaveBeenCalledWith("0xmockaddress");
|
|
514
|
-
expect(adapter.connected).toBe(true);
|
|
515
|
-
expect(adapter.address).toBe("0xmockaddress");
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
it("ignores a concurrent connect() while one is still in flight", async () => {
|
|
519
|
-
let releaseFirstConnect: () => void = () => {};
|
|
520
|
-
const mockWallet = createMockWallet({
|
|
521
|
-
connect: vi.fn(
|
|
522
|
-
() =>
|
|
523
|
-
new Promise<void>((resolve) => {
|
|
524
|
-
releaseFirstConnect = resolve;
|
|
525
|
-
})
|
|
526
|
-
),
|
|
527
|
-
});
|
|
528
|
-
(window as any).midenWallet = mockWallet;
|
|
529
|
-
|
|
530
|
-
const adapter = new MidenWalletAdapter();
|
|
531
|
-
adapter.readyState = WalletReadyState.Installed;
|
|
532
|
-
|
|
533
|
-
// First connect stays in flight (its wallet.connect() promise is pending).
|
|
534
|
-
const first = adapter.connect(
|
|
535
|
-
PrivateDataPermission.UponRequest,
|
|
536
|
-
WalletAdapterNetwork.Testnet
|
|
537
|
-
);
|
|
538
|
-
// A second call while `connecting` is true must be a guarded no-op.
|
|
539
|
-
await adapter.connect(
|
|
540
|
-
PrivateDataPermission.UponRequest,
|
|
541
|
-
WalletAdapterNetwork.Testnet
|
|
542
|
-
);
|
|
543
|
-
expect(mockWallet.connect).toHaveBeenCalledTimes(1);
|
|
544
|
-
|
|
545
|
-
releaseFirstConnect();
|
|
546
|
-
await first;
|
|
547
|
-
expect(adapter.connected).toBe(true);
|
|
548
|
-
});
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
describe("disconnect", () => {
|
|
552
|
-
it("clears address, publicKey, and emits disconnect", async () => {
|
|
553
|
-
const { adapter } = await createConnectedAdapter();
|
|
554
|
-
|
|
555
|
-
const disconnectHandler = vi.fn();
|
|
556
|
-
adapter.on("disconnect", disconnectHandler);
|
|
557
|
-
|
|
558
|
-
await adapter.disconnect();
|
|
559
|
-
|
|
560
|
-
expect(adapter.address).toBeNull();
|
|
561
|
-
expect(adapter.publicKey).toBeNull();
|
|
562
|
-
expect(adapter.connected).toBe(false);
|
|
563
|
-
expect(disconnectHandler).toHaveBeenCalled();
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
it("emits error if wallet.disconnect fails", async () => {
|
|
567
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
568
|
-
mockWallet.disconnect.mockRejectedValue(new Error("disconnect failed"));
|
|
569
|
-
|
|
570
|
-
const errorHandler = vi.fn();
|
|
571
|
-
adapter.on("error", errorHandler);
|
|
572
|
-
|
|
573
|
-
await adapter.disconnect();
|
|
574
|
-
expect(errorHandler).toHaveBeenCalled();
|
|
575
|
-
});
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
describe("error wrapping", () => {
|
|
579
|
-
it("wraps wallet method failures in WalletTransactionError", async () => {
|
|
580
|
-
const { adapter, mockWallet } = await createConnectedAdapter();
|
|
581
|
-
mockWallet.requestSend.mockRejectedValue(new Error("network error"));
|
|
582
|
-
|
|
583
|
-
await expect(
|
|
584
|
-
adapter.requestSend({
|
|
585
|
-
senderAddress: "s",
|
|
586
|
-
recipientAddress: "r",
|
|
587
|
-
faucetId: "f",
|
|
588
|
-
noteType: "public",
|
|
589
|
-
amount: 1,
|
|
590
|
-
})
|
|
591
|
-
).rejects.toThrow(WalletTransactionError);
|
|
592
|
-
});
|
|
593
|
-
});
|
|
594
|
-
});
|