@walletmesh/aztec-rpc-wallet 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }