@walletmesh/aztec-rpc-wallet 0.1.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 (108) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +201 -0
  3. package/README.md +260 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/aztecRemoteWallet.d.ts +73 -0
  6. package/dist/aztecRemoteWallet.d.ts.map +1 -0
  7. package/dist/aztecRemoteWallet.js +354 -0
  8. package/dist/chainProvider.d.ts +56 -0
  9. package/dist/chainProvider.d.ts.map +1 -0
  10. package/dist/chainProvider.js +98 -0
  11. package/dist/contractArtifactCache.d.ts +50 -0
  12. package/dist/contractArtifactCache.d.ts.map +1 -0
  13. package/dist/contractArtifactCache.js +66 -0
  14. package/dist/errors.d.ts +50 -0
  15. package/dist/errors.d.ts.map +1 -0
  16. package/dist/errors.js +62 -0
  17. package/dist/handlers/aztecAccountWallet.d.ts +4 -0
  18. package/dist/handlers/aztecAccountWallet.d.ts.map +1 -0
  19. package/dist/handlers/aztecAccountWallet.js +329 -0
  20. package/dist/handlers/transactions.d.ts +21 -0
  21. package/dist/handlers/transactions.d.ts.map +1 -0
  22. package/dist/handlers/transactions.js +90 -0
  23. package/dist/handlers.d.ts +27 -0
  24. package/dist/handlers.d.ts.map +1 -0
  25. package/dist/handlers.js +55 -0
  26. package/dist/index.d.ts +58 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +33 -0
  29. package/dist/provider.d.ts +105 -0
  30. package/dist/provider.d.ts.map +1 -0
  31. package/dist/provider.js +160 -0
  32. package/dist/serializers/account.d.ts +167 -0
  33. package/dist/serializers/account.d.ts.map +1 -0
  34. package/dist/serializers/account.js +245 -0
  35. package/dist/serializers/contract-utils.d.ts +40 -0
  36. package/dist/serializers/contract-utils.d.ts.map +1 -0
  37. package/dist/serializers/contract-utils.js +102 -0
  38. package/dist/serializers/contract.d.ts +168 -0
  39. package/dist/serializers/contract.d.ts.map +1 -0
  40. package/dist/serializers/contract.js +268 -0
  41. package/dist/serializers/core.d.ts +110 -0
  42. package/dist/serializers/core.d.ts.map +1 -0
  43. package/dist/serializers/core.js +130 -0
  44. package/dist/serializers/index.d.ts +28 -0
  45. package/dist/serializers/index.d.ts.map +1 -0
  46. package/dist/serializers/index.js +159 -0
  47. package/dist/serializers/log.d.ts +113 -0
  48. package/dist/serializers/log.d.ts.map +1 -0
  49. package/dist/serializers/log.js +231 -0
  50. package/dist/serializers/note.d.ts +127 -0
  51. package/dist/serializers/note.d.ts.map +1 -0
  52. package/dist/serializers/note.js +182 -0
  53. package/dist/serializers/transaction-utils.d.ts +107 -0
  54. package/dist/serializers/transaction-utils.d.ts.map +1 -0
  55. package/dist/serializers/transaction-utils.js +130 -0
  56. package/dist/serializers/transaction.d.ts +103 -0
  57. package/dist/serializers/transaction.d.ts.map +1 -0
  58. package/dist/serializers/transaction.js +238 -0
  59. package/dist/serializers/types.d.ts +49 -0
  60. package/dist/serializers/types.d.ts.map +1 -0
  61. package/dist/serializers/types.js +22 -0
  62. package/dist/types.d.ts +391 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +8 -0
  65. package/dist/wallet.d.ts +62 -0
  66. package/dist/wallet.d.ts.map +1 -0
  67. package/dist/wallet.js +77 -0
  68. package/package.json +44 -0
  69. package/src/aztecRemoteWallet.test.ts +542 -0
  70. package/src/aztecRemoteWallet.ts +484 -0
  71. package/src/chainProvider.test.ts +322 -0
  72. package/src/chainProvider.ts +122 -0
  73. package/src/contractArtifactCache.test.ts +126 -0
  74. package/src/contractArtifactCache.ts +75 -0
  75. package/src/errors.ts +71 -0
  76. package/src/handlers/aztecAccountWallet.test.ts +720 -0
  77. package/src/handlers/aztecAccountWallet.ts +593 -0
  78. package/src/handlers/transactions.ts +110 -0
  79. package/src/handlers.test.ts +270 -0
  80. package/src/handlers.ts +70 -0
  81. package/src/index.test.ts +23 -0
  82. package/src/index.ts +64 -0
  83. package/src/provider.test.ts +276 -0
  84. package/src/provider.ts +189 -0
  85. package/src/serializers/account.test.ts +125 -0
  86. package/src/serializers/account.ts +319 -0
  87. package/src/serializers/contract-utils.ts +104 -0
  88. package/src/serializers/contract.test.ts +162 -0
  89. package/src/serializers/contract.ts +350 -0
  90. package/src/serializers/core.test.ts +56 -0
  91. package/src/serializers/core.ts +141 -0
  92. package/src/serializers/index.test.ts +122 -0
  93. package/src/serializers/index.ts +213 -0
  94. package/src/serializers/log.test.ts +119 -0
  95. package/src/serializers/log.ts +283 -0
  96. package/src/serializers/note.test.ts +100 -0
  97. package/src/serializers/note.ts +227 -0
  98. package/src/serializers/transaction-utils.ts +237 -0
  99. package/src/serializers/transaction.test.ts +153 -0
  100. package/src/serializers/transaction.ts +342 -0
  101. package/src/serializers/types.ts +58 -0
  102. package/src/types.ts +295 -0
  103. package/src/wallet.test.ts +275 -0
  104. package/src/wallet.ts +94 -0
  105. package/tsconfig.build.json +6 -0
  106. package/tsconfig.json +11 -0
  107. package/typedoc.json +15 -0
  108. package/vitest.config.ts +10 -0
@@ -0,0 +1,322 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import type { JSONRPCTransport, JSONRPCRequest } from '@walletmesh/jsonrpc';
3
+ import { AztecChainProvider } from './chainProvider.js';
4
+ import { AztecWalletError } from './errors.js';
5
+ import type { ContractInstanceWithAddress, ContractArtifact, AztecAddress } from '@aztec/aztec.js';
6
+ import type { AztecWalletMethodMap } from './types.js';
7
+
8
+ const createMockTransport = () => ({
9
+ send: vi.fn().mockImplementation(() => Promise.resolve()),
10
+ });
11
+
12
+ describe('AztecChainProvider', () => {
13
+ let provider: AztecChainProvider;
14
+ let mockTransport: ReturnType<typeof createMockTransport>;
15
+
16
+ beforeEach(() => {
17
+ mockTransport = createMockTransport();
18
+ provider = new AztecChainProvider(mockTransport);
19
+ });
20
+
21
+ afterEach(() => {
22
+ vi.clearAllMocks();
23
+ });
24
+
25
+ describe('getAccount', () => {
26
+ it('returns account address on success', async () => {
27
+ const expectedAddress = '0x1234567890abcdef';
28
+ const promise = provider.getAccount();
29
+
30
+ // Get the request ID from the sent message
31
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
32
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_getAccount'>],
33
+ ];
34
+ expect(sentRequest).toEqual({
35
+ jsonrpc: '2.0',
36
+ id: expect.any(String),
37
+ method: 'aztec_getAccount',
38
+ });
39
+
40
+ // Simulate response
41
+ await provider.receiveMessage({
42
+ jsonrpc: '2.0',
43
+ result: expectedAddress,
44
+ id: sentRequest.id,
45
+ });
46
+
47
+ const result = await promise;
48
+ expect(result).toBe(expectedAddress);
49
+ });
50
+
51
+ it('throws on invalid response', async () => {
52
+ const promise = provider.getAccount();
53
+
54
+ // Get the request ID
55
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
56
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_getAccount'>],
57
+ ];
58
+
59
+ // Simulate error response
60
+ await provider.receiveMessage({
61
+ jsonrpc: '2.0',
62
+ result: null,
63
+ id: sentRequest.id,
64
+ });
65
+
66
+ await expect(promise).rejects.toThrow(AztecWalletError);
67
+ });
68
+ });
69
+
70
+ describe('sendTransaction', () => {
71
+ const txParams = {
72
+ functionCalls: [
73
+ {
74
+ contractAddress: '0x1234',
75
+ functionName: 'transfer',
76
+ args: ['0x5678', 100],
77
+ },
78
+ ],
79
+ };
80
+
81
+ it('returns transaction hash on success', async () => {
82
+ const expectedHash = '0xabcd';
83
+ const promise = provider.sendTransaction(txParams);
84
+
85
+ // Get the request ID
86
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
87
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_sendTransaction'>],
88
+ ];
89
+ expect(sentRequest).toEqual({
90
+ jsonrpc: '2.0',
91
+ id: expect.any(String),
92
+ method: 'aztec_sendTransaction',
93
+ params: txParams,
94
+ });
95
+
96
+ // Simulate response
97
+ await provider.receiveMessage({
98
+ jsonrpc: '2.0',
99
+ result: expectedHash,
100
+ id: sentRequest.id,
101
+ });
102
+
103
+ const result = await promise;
104
+ expect(result).toBe(expectedHash);
105
+ });
106
+
107
+ it('throws on invalid response', async () => {
108
+ const promise = provider.sendTransaction(txParams);
109
+
110
+ // Get the request ID
111
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
112
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_sendTransaction'>],
113
+ ];
114
+
115
+ // Simulate error response
116
+ await provider.receiveMessage({
117
+ jsonrpc: '2.0',
118
+ result: null,
119
+ id: sentRequest.id,
120
+ });
121
+
122
+ await expect(promise).rejects.toThrow(AztecWalletError);
123
+ });
124
+ });
125
+
126
+ describe('simulateTransaction', () => {
127
+ const txParams = {
128
+ contractAddress: '0x1234',
129
+ functionName: 'transfer',
130
+ args: ['0x5678', 100],
131
+ };
132
+
133
+ it('returns simulation result on success', async () => {
134
+ const expectedResult = { success: true, data: 'test' };
135
+ const promise = provider.simulateTransaction(txParams);
136
+
137
+ // Get the request ID
138
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
139
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_simulateTransaction'>],
140
+ ];
141
+ expect(sentRequest).toEqual({
142
+ jsonrpc: '2.0',
143
+ id: expect.any(String),
144
+ method: 'aztec_simulateTransaction',
145
+ params: txParams,
146
+ });
147
+
148
+ // Simulate response
149
+ await provider.receiveMessage({
150
+ jsonrpc: '2.0',
151
+ result: expectedResult,
152
+ id: sentRequest.id,
153
+ });
154
+
155
+ const result = await promise;
156
+ expect(result).toEqual(expectedResult);
157
+ });
158
+
159
+ it('throws on invalid response', async () => {
160
+ const promise = provider.simulateTransaction(txParams);
161
+
162
+ // Get the request ID
163
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
164
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_simulateTransaction'>],
165
+ ];
166
+
167
+ // Simulate error response
168
+ await provider.receiveMessage({
169
+ jsonrpc: '2.0',
170
+ result: null,
171
+ id: sentRequest.id,
172
+ });
173
+
174
+ await expect(promise).rejects.toThrow(AztecWalletError);
175
+ });
176
+ });
177
+
178
+ describe('registerContract', () => {
179
+ const contractParams = {
180
+ instance: { address: '0x1234' as unknown as AztecAddress } as ContractInstanceWithAddress,
181
+ artifact: { name: 'TestContract' } as ContractArtifact,
182
+ };
183
+
184
+ it('succeeds on valid response', async () => {
185
+ const promise = provider.registerContract(contractParams);
186
+
187
+ // Get the request ID
188
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
189
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_registerContract'>],
190
+ ];
191
+ expect(sentRequest).toEqual({
192
+ jsonrpc: '2.0',
193
+ id: expect.any(String),
194
+ method: 'aztec_registerContract',
195
+ params: contractParams,
196
+ });
197
+
198
+ // Simulate response
199
+ await provider.receiveMessage({
200
+ jsonrpc: '2.0',
201
+ result: true,
202
+ id: sentRequest.id,
203
+ });
204
+
205
+ await expect(promise).resolves.toBeUndefined();
206
+ });
207
+
208
+ it('throws on invalid response', async () => {
209
+ const promise = provider.registerContract(contractParams);
210
+
211
+ // Get the request ID
212
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
213
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_registerContract'>],
214
+ ];
215
+
216
+ // Simulate error response
217
+ await provider.receiveMessage({
218
+ jsonrpc: '2.0',
219
+ result: false,
220
+ id: sentRequest.id,
221
+ });
222
+
223
+ await expect(promise).rejects.toThrow(AztecWalletError);
224
+ });
225
+ });
226
+
227
+ describe('registerContractClass', () => {
228
+ const classParams = {
229
+ artifact: { name: 'TestContract' } as ContractArtifact,
230
+ };
231
+
232
+ it('succeeds on valid response', async () => {
233
+ const promise = provider.registerContractClass(classParams);
234
+
235
+ // Get the request ID
236
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
237
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_registerContractClass'>],
238
+ ];
239
+ expect(sentRequest).toEqual({
240
+ jsonrpc: '2.0',
241
+ id: expect.any(String),
242
+ method: 'aztec_registerContractClass',
243
+ params: classParams,
244
+ });
245
+
246
+ // Simulate response
247
+ await provider.receiveMessage({
248
+ jsonrpc: '2.0',
249
+ result: true,
250
+ id: sentRequest.id,
251
+ });
252
+
253
+ await expect(promise).resolves.toBeUndefined();
254
+ });
255
+
256
+ it('throws on invalid response', async () => {
257
+ const promise = provider.registerContractClass(classParams);
258
+
259
+ // Get the request ID
260
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
261
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_registerContractClass'>],
262
+ ];
263
+
264
+ // Simulate error response
265
+ await provider.receiveMessage({
266
+ jsonrpc: '2.0',
267
+ result: false,
268
+ id: sentRequest.id,
269
+ });
270
+
271
+ await expect(promise).rejects.toThrow(AztecWalletError);
272
+ });
273
+ });
274
+
275
+ describe('registerSender', () => {
276
+ const senderParams = {
277
+ sender: '0x1234' as unknown as AztecAddress,
278
+ };
279
+
280
+ it('succeeds on valid response', async () => {
281
+ const promise = provider.registerSender(senderParams);
282
+
283
+ // Get the request ID
284
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
285
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_registerSender'>],
286
+ ];
287
+ expect(sentRequest).toEqual({
288
+ jsonrpc: '2.0',
289
+ id: expect.any(String),
290
+ method: 'aztec_registerSender',
291
+ params: senderParams,
292
+ });
293
+
294
+ // Simulate response
295
+ await provider.receiveMessage({
296
+ jsonrpc: '2.0',
297
+ result: '0x1234',
298
+ id: sentRequest.id,
299
+ });
300
+
301
+ await expect(promise).resolves.toBeUndefined();
302
+ });
303
+
304
+ it('throws on invalid response', async () => {
305
+ const promise = provider.registerSender(senderParams);
306
+
307
+ // Get the request ID
308
+ const [[sentRequest]] = mockTransport.send.mock.calls as [
309
+ [JSONRPCRequest<AztecWalletMethodMap, 'aztec_registerSender'>],
310
+ ];
311
+
312
+ // Simulate error response
313
+ await provider.receiveMessage({
314
+ jsonrpc: '2.0',
315
+ result: null,
316
+ id: sentRequest.id,
317
+ });
318
+
319
+ await expect(promise).rejects.toThrow(AztecWalletError);
320
+ });
321
+ });
322
+ });
@@ -0,0 +1,122 @@
1
+ import { JSONRPCNode } from '@walletmesh/jsonrpc';
2
+ import type { JSONRPCTransport } from '@walletmesh/jsonrpc';
3
+ import { AztecWalletError, AztecWalletErrorType } from './errors.js';
4
+ import type {
5
+ AztecChainId,
6
+ AztecWalletMethodMap,
7
+ TransactionParams,
8
+ TransactionFunctionCall,
9
+ } from './types.js';
10
+ import type { ContractInstanceWithAddress, ContractArtifact } from '@aztec/aztec.js';
11
+
12
+ /**
13
+ * Provider for directly interacting with an Aztec chain wallet.
14
+ * This is a minimal implementation that supports core Aztec operations
15
+ * without the complexity of multi-chain routing.
16
+ */
17
+ export class AztecChainProvider extends JSONRPCNode<AztecWalletMethodMap> {
18
+ protected async makeRequest<M extends keyof AztecWalletMethodMap>(
19
+ method: M,
20
+ params?: AztecWalletMethodMap[M]['params'],
21
+ ): Promise<AztecWalletMethodMap[M]['result']> {
22
+ try {
23
+ return await this.callMethod(method, params);
24
+ } catch (error) {
25
+ throw new AztecWalletError(
26
+ AztecWalletErrorType.invalidResponse,
27
+ error instanceof Error ? error.message : 'Invalid response received',
28
+ );
29
+ }
30
+ }
31
+
32
+ public async connect(): Promise<boolean> {
33
+ try {
34
+ await this.makeRequest('aztec_connect');
35
+ return true;
36
+ } catch (error) {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Gets the account address from the wallet.
43
+ * @returns The account address as a string
44
+ * @throws {AztecWalletError} If response is invalid
45
+ */
46
+ public async getAccount(): Promise<string> {
47
+ const result = await this.makeRequest('aztec_getAccount');
48
+ if (typeof result !== 'string' || !result) {
49
+ throw new AztecWalletError(AztecWalletErrorType.invalidResponse, 'Invalid account address returned');
50
+ }
51
+ return result;
52
+ }
53
+
54
+ /**
55
+ * Sends a transaction to the chain.
56
+ * @param params - Transaction parameters including function calls
57
+ * @returns Transaction hash
58
+ * @throws {AztecWalletError} If transaction fails or response invalid
59
+ */
60
+ public async sendTransaction(params: TransactionParams): Promise<string> {
61
+ const result = await this.makeRequest('aztec_sendTransaction', params);
62
+ if (typeof result !== 'string' || !result) {
63
+ throw new AztecWalletError(AztecWalletErrorType.invalidResponse, 'Invalid transaction hash returned');
64
+ }
65
+ return result;
66
+ }
67
+
68
+ /**
69
+ * Simulates a transaction without submitting it.
70
+ * @param params - Transaction parameters to simulate
71
+ * @returns Simulation result
72
+ * @throws {AztecWalletError} If simulation fails
73
+ */
74
+ public async simulateTransaction(params: TransactionFunctionCall): Promise<unknown> {
75
+ const result = await this.makeRequest('aztec_simulateTransaction', params);
76
+ if (result === undefined || result === null) {
77
+ throw new AztecWalletError(AztecWalletErrorType.invalidResponse, 'Invalid simulation result returned');
78
+ }
79
+ return result;
80
+ }
81
+
82
+ /**
83
+ * Registers a contract instance with the wallet.
84
+ * @param params - Contract registration parameters
85
+ * @throws {AztecWalletError} If registration fails
86
+ */
87
+ public async registerContract(params: {
88
+ instance: ContractInstanceWithAddress;
89
+ artifact?: ContractArtifact;
90
+ }): Promise<void> {
91
+ const result = await this.makeRequest('aztec_registerContract', params);
92
+ if (result !== true) {
93
+ throw new AztecWalletError(AztecWalletErrorType.invalidResponse, 'Contract registration failed');
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Registers a contract class with the wallet.
99
+ * @param params - Contract class registration parameters
100
+ * @throws {AztecWalletError} If registration fails
101
+ */
102
+ public async registerContractClass(params: {
103
+ artifact: ContractArtifact;
104
+ }): Promise<void> {
105
+ const result = await this.makeRequest('aztec_registerContractClass', params);
106
+ if (result !== true) {
107
+ throw new AztecWalletError(AztecWalletErrorType.invalidResponse, 'Contract class registration failed');
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Registers a sender with the wallet.
113
+ * @param params - Sender registration parameters
114
+ * @throws {AztecWalletError} If registration fails
115
+ */
116
+ public async registerSender(params: AztecWalletMethodMap['aztec_registerSender']['params']): Promise<void> {
117
+ const result = await this.makeRequest('aztec_registerSender', params);
118
+ if (!result) {
119
+ throw new AztecWalletError(AztecWalletErrorType.invalidResponse, 'Sender registration failed');
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,126 @@
1
+ import { describe, expect, it, vi, beforeEach, type Mock } from 'vitest';
2
+ import type { AztecAddress, ContractArtifact, ContractClassWithId, Wallet } from '@aztec/aztec.js';
3
+ import { ContractArtifactCache } from './contractArtifactCache.js';
4
+ import { AztecWalletError } from './errors.js';
5
+
6
+ describe('ContractArtifactCache', () => {
7
+ let wallet: Wallet;
8
+ let cache: ContractArtifactCache;
9
+ let mockContractAddress: AztecAddress;
10
+ let mockContractClassId: ContractClassWithId;
11
+ let mockArtifact: ContractArtifact;
12
+
13
+ beforeEach(() => {
14
+ // Mock contract address and class ID
15
+ // Create a complete mock of AztecAddress
16
+ mockContractAddress = {
17
+ toString: () => '0x123',
18
+ _branding: 'aztec.Address',
19
+ xCoord: BigInt(0),
20
+ size: 32,
21
+ equals: () => false,
22
+ toBuffer: () => Buffer.from([]),
23
+ toFields: () => [],
24
+ toField: () => BigInt(0),
25
+ toBigInt: () => BigInt(0),
26
+ isZero: () => false,
27
+ isValid: () => true,
28
+ toAddressPoint: () => ({ x: BigInt(0) }),
29
+ toJSON: () => ({ x: '0' }),
30
+ [Symbol.for('nodejs.util.inspect.custom')]: () => 'AztecAddress',
31
+ } as unknown as AztecAddress;
32
+
33
+ // Create a complete mock of ContractClassWithId
34
+ mockContractClassId = {
35
+ toString: () => '456',
36
+ id: BigInt(456),
37
+ version: 1,
38
+ artifactHash: BigInt(0),
39
+ privateFunctions: new Map(),
40
+ publicFunctions: new Map(),
41
+ packedBytecode: new Uint8Array(),
42
+ } as unknown as ContractClassWithId;
43
+ mockArtifact = { name: 'TestContract' } as ContractArtifact;
44
+
45
+ // Create mock wallet
46
+ wallet = {
47
+ getContractInstance: vi.fn(),
48
+ getContractArtifact: vi.fn(),
49
+ } as unknown as Wallet;
50
+
51
+ // Initialize cache with mock wallet
52
+ cache = new ContractArtifactCache(wallet);
53
+ });
54
+
55
+ it('returns cached artifact if available', async () => {
56
+ // Setup: Pre-populate cache
57
+ (cache as unknown as { cache: Map<string, ContractArtifact> }).cache.set(
58
+ mockContractAddress.toString(),
59
+ mockArtifact,
60
+ );
61
+
62
+ // Test: Get artifact
63
+ const result = await cache.getContractArtifact(mockContractAddress);
64
+
65
+ // Verify: Result is from cache, wallet methods not called
66
+ expect(result).toBe(mockArtifact);
67
+ expect(wallet.getContractInstance).not.toHaveBeenCalled();
68
+ expect(wallet.getContractArtifact).not.toHaveBeenCalled();
69
+ });
70
+
71
+ it('fetches and caches artifact on cache miss', async () => {
72
+ // Setup: Configure wallet mocks
73
+ (wallet.getContractInstance as Mock).mockResolvedValue({ contractClassId: mockContractClassId });
74
+ (wallet.getContractArtifact as Mock).mockResolvedValue(mockArtifact);
75
+
76
+ // Test: Get artifact
77
+ const result = await cache.getContractArtifact(mockContractAddress);
78
+
79
+ // Verify: Correct methods called and result cached
80
+ expect(wallet.getContractInstance).toHaveBeenCalledWith(mockContractAddress);
81
+ expect(wallet.getContractArtifact).toHaveBeenCalledWith(mockContractClassId);
82
+ expect(result).toBe(mockArtifact);
83
+
84
+ // Verify: Result was cached
85
+ expect(
86
+ (cache as unknown as { cache: Map<string, ContractArtifact> }).cache.get(
87
+ mockContractAddress.toString(),
88
+ ),
89
+ ).toBe(mockArtifact);
90
+ });
91
+
92
+ it('throws error if contract instance not found', async () => {
93
+ // Setup: Mock contract instance not found
94
+ (wallet.getContractInstance as Mock).mockResolvedValue(null);
95
+
96
+ // Test & Verify: Expect error
97
+ await expect(cache.getContractArtifact(mockContractAddress)).rejects.toThrow(
98
+ new AztecWalletError('contractInstanceNotRegistered', mockContractAddress.toString()),
99
+ );
100
+
101
+ // Verify: Nothing cached
102
+ expect(
103
+ (cache as unknown as { cache: Map<string, ContractArtifact> }).cache.get(
104
+ mockContractAddress.toString(),
105
+ ),
106
+ ).toBeUndefined();
107
+ });
108
+
109
+ it('throws error if contract artifact not found', async () => {
110
+ // Setup: Mock contract instance found but artifact missing
111
+ (wallet.getContractInstance as Mock).mockResolvedValue({ contractClassId: mockContractClassId });
112
+ (wallet.getContractArtifact as Mock).mockResolvedValue(null);
113
+
114
+ // Test & Verify: Expect error
115
+ await expect(cache.getContractArtifact(mockContractAddress)).rejects.toThrow(
116
+ new AztecWalletError('contractClassNotRegistered', mockContractClassId.toString()),
117
+ );
118
+
119
+ // Verify: Nothing cached
120
+ expect(
121
+ (cache as unknown as { cache: Map<string, ContractArtifact> }).cache.get(
122
+ mockContractAddress.toString(),
123
+ ),
124
+ ).toBeUndefined();
125
+ });
126
+ });
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @module contractArtifactCache
3
+ *
4
+ * This module provides caching functionality for Aztec contract artifacts.
5
+ * It helps improve performance by avoiding repeated fetches of the same contract artifacts.
6
+ */
7
+
8
+ import type { AztecAddress, ContractArtifact, Wallet } from '@aztec/aztec.js';
9
+
10
+ import { AztecWalletError } from './errors.js';
11
+
12
+ /**
13
+ * Caches contract artifacts to optimize contract interactions.
14
+ *
15
+ * This class maintains an in-memory cache of contract artifacts indexed by contract address.
16
+ * When a contract artifact is requested:
17
+ * 1. First checks the cache for an existing artifact
18
+ * 2. If not found, fetches the contract instance and its artifact from the wallet
19
+ * 3. Stores the artifact in the cache for future use
20
+ * 4. Returns the artifact to the caller
21
+ *
22
+ * This caching mechanism helps reduce:
23
+ * - Network requests to fetch contract data
24
+ * - Processing overhead of parsing contract artifacts
25
+ * - Memory usage by reusing existing artifacts
26
+ */
27
+ export class ContractArtifactCache {
28
+ /** Map of contract addresses to their artifacts */
29
+ private cache = new Map<string, ContractArtifact>();
30
+ /** Reference to the wallet instance for fetching contract data */
31
+ private wallet: Wallet;
32
+
33
+ /**
34
+ * Creates a new ContractArtifactCache instance.
35
+ * @param wallet - Wallet instance used to fetch contract data when cache misses occur
36
+ */
37
+ constructor(wallet: Wallet) {
38
+ this.wallet = wallet;
39
+ }
40
+
41
+ /**
42
+ * Retrieves the contract artifact for a given contract address.
43
+ * First checks the cache, then falls back to fetching from the wallet if needed.
44
+ *
45
+ * The process:
46
+ * 1. Check if artifact exists in cache
47
+ * 2. If not, get contract instance from wallet
48
+ * 3. Use instance to get contract class ID
49
+ * 4. Fetch artifact using class ID
50
+ * 5. Cache the artifact for future use
51
+ *
52
+ * @param contractAddress - The contract address to retrieve the artifact for
53
+ * @returns Promise resolving to the contract artifact
54
+ * @throws {AztecWalletError} If contract instance or class not registered
55
+ */
56
+ public async getContractArtifact(contractAddress: AztecAddress): Promise<ContractArtifact> {
57
+ const addressStr = contractAddress.toString();
58
+ const cached = this.cache.get(addressStr);
59
+ if (cached) {
60
+ return cached;
61
+ }
62
+
63
+ const contract = await this.wallet.getContractInstance(contractAddress);
64
+ if (!contract) {
65
+ throw new AztecWalletError('contractInstanceNotRegistered', addressStr);
66
+ }
67
+ const artifact = await this.wallet.getContractArtifact(contract.contractClassId);
68
+ if (!artifact) {
69
+ throw new AztecWalletError('contractClassNotRegistered', contract.contractClassId.toString());
70
+ }
71
+
72
+ this.cache.set(addressStr, artifact);
73
+ return artifact;
74
+ }
75
+ }