@walletmesh/aztec-rpc-wallet 0.3.0 → 0.4.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.
- package/CHANGELOG.md +26 -0
- package/README.md +290 -228
- package/dist/.tsbuildinfo +1 -1
- package/dist/client/aztec-dapp-wallet.d.ts +401 -0
- package/dist/client/aztec-dapp-wallet.d.ts.map +1 -0
- package/dist/client/aztec-dapp-wallet.js +705 -0
- package/dist/client/aztec-router-provider.d.ts +58 -0
- package/dist/client/aztec-router-provider.d.ts.map +1 -0
- package/dist/client/aztec-router-provider.js +62 -0
- package/dist/client/helpers.d.ts +44 -0
- package/dist/client/helpers.d.ts.map +1 -0
- package/dist/client/helpers.js +79 -0
- package/dist/client/register-serializers.d.ts +41 -0
- package/dist/client/register-serializers.d.ts.map +1 -0
- package/dist/client/register-serializers.js +97 -0
- package/dist/contractArtifactCache.d.ts +49 -32
- package/dist/contractArtifactCache.d.ts.map +1 -1
- package/dist/contractArtifactCache.js +47 -34
- package/dist/errors.d.ts +50 -8
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +50 -10
- package/dist/index.d.ts +53 -40
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -17
- package/dist/types.d.ts +345 -268
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -8
- package/dist/wallet/create-node.d.ts +73 -0
- package/dist/wallet/create-node.d.ts.map +1 -0
- package/dist/wallet/create-node.js +99 -0
- package/dist/wallet/handlers/account.d.ts +67 -0
- package/dist/wallet/handlers/account.d.ts.map +1 -0
- package/dist/wallet/handlers/account.js +85 -0
- package/dist/wallet/handlers/contract-interaction.d.ts +77 -0
- package/dist/wallet/handlers/contract-interaction.d.ts.map +1 -0
- package/dist/wallet/handlers/contract-interaction.js +219 -0
- package/dist/wallet/handlers/contract.d.ts +96 -0
- package/dist/wallet/handlers/contract.d.ts.map +1 -0
- package/dist/wallet/handlers/contract.js +146 -0
- package/dist/wallet/handlers/event.d.ts +62 -0
- package/dist/wallet/handlers/event.d.ts.map +1 -0
- package/dist/wallet/handlers/event.js +85 -0
- package/dist/wallet/handlers/index.d.ts +88 -0
- package/dist/wallet/handlers/index.d.ts.map +1 -0
- package/dist/wallet/handlers/index.js +47 -0
- package/dist/wallet/handlers/node.d.ts +117 -0
- package/dist/wallet/handlers/node.d.ts.map +1 -0
- package/dist/wallet/handlers/node.js +196 -0
- package/dist/wallet/handlers/senders.d.ts +70 -0
- package/dist/wallet/handlers/senders.d.ts.map +1 -0
- package/dist/wallet/handlers/senders.js +92 -0
- package/dist/wallet/handlers/transaction.d.ts +123 -0
- package/dist/wallet/handlers/transaction.d.ts.map +1 -0
- package/dist/wallet/handlers/transaction.js +191 -0
- package/dist/wallet/serializers.d.ts +75 -0
- package/dist/wallet/serializers.d.ts.map +1 -0
- package/dist/wallet/serializers.js +501 -0
- package/docs/README.md +290 -229
- package/docs/classes/AztecDappWallet.md +1304 -0
- package/docs/classes/AztecRouterProvider.md +1058 -0
- package/docs/classes/AztecWalletError.md +124 -47
- package/docs/classes/ContractArtifactCache.md +48 -31
- package/docs/functions/connectAztec.md +62 -0
- package/docs/functions/createAztecWallet.md +46 -0
- package/docs/functions/createAztecWalletNode.md +96 -0
- package/docs/functions/registerAztecSerializers.md +61 -0
- package/docs/functions/registerWalletAztecSerializers.md +39 -0
- package/docs/globals.md +16 -14
- package/docs/interfaces/AztecHandlerContext.md +54 -0
- package/docs/interfaces/AztecWalletContext.md +50 -0
- package/docs/interfaces/AztecWalletMethodMap.md +471 -470
- package/docs/type-aliases/AztecChainId.md +16 -3
- package/docs/variables/ALL_AZTEC_METHODS.md +20 -0
- package/docs/variables/AztecWalletErrorMap.md +9 -4
- package/docs/variables/AztecWalletSerializer.md +45 -0
- package/package.json +9 -9
- package/src/client/aztec-dapp-wallet.test.ts +628 -0
- package/src/client/aztec-dapp-wallet.ts +879 -0
- package/src/client/aztec-router-provider.test.ts +235 -0
- package/src/client/aztec-router-provider.ts +64 -0
- package/src/client/helpers.test.ts +187 -0
- package/src/client/helpers.ts +91 -0
- package/src/client/register-serializers.ts +108 -0
- package/src/contractArtifactCache.test.ts +21 -10
- package/src/contractArtifactCache.ts +54 -35
- package/src/errors.ts +58 -10
- package/src/index.test.ts +2 -6
- package/src/index.ts +73 -37
- package/src/types.ts +379 -217
- package/src/wallet/create-node.test.ts +332 -0
- package/src/wallet/create-node.ts +120 -0
- package/src/wallet/handlers/account.test.ts +172 -0
- package/src/wallet/handlers/account.ts +99 -0
- package/src/wallet/handlers/contract-interaction.test.ts +248 -0
- package/src/wallet/handlers/contract-interaction.ts +269 -0
- package/src/wallet/handlers/contract.test.ts +245 -0
- package/src/wallet/handlers/contract.ts +174 -0
- package/src/wallet/handlers/event.test.ts +216 -0
- package/src/wallet/handlers/event.ts +99 -0
- package/src/wallet/handlers/index.ts +84 -0
- package/src/wallet/handlers/node.test.ts +304 -0
- package/src/wallet/handlers/node.ts +230 -0
- package/src/wallet/handlers/senders.test.ts +172 -0
- package/src/wallet/handlers/senders.ts +106 -0
- package/src/wallet/handlers/transaction.test.ts +371 -0
- package/src/wallet/handlers/transaction.ts +239 -0
- package/src/wallet/serializers.test.ts +253 -0
- package/src/wallet/serializers.ts +586 -0
- package/typedoc.json +23 -1
- package/dist/aztecRemoteWallet.d.ts +0 -70
- package/dist/aztecRemoteWallet.d.ts.map +0 -1
- package/dist/aztecRemoteWallet.js +0 -335
- package/dist/chainProvider.d.ts +0 -56
- package/dist/chainProvider.d.ts.map +0 -1
- package/dist/chainProvider.js +0 -98
- package/dist/handlers/aztecAccountWallet.d.ts +0 -4
- package/dist/handlers/aztecAccountWallet.d.ts.map +0 -1
- package/dist/handlers/aztecAccountWallet.js +0 -296
- package/dist/handlers/transactions.d.ts +0 -21
- package/dist/handlers/transactions.d.ts.map +0 -1
- package/dist/handlers/transactions.js +0 -98
- package/dist/handlers.d.ts +0 -27
- package/dist/handlers.d.ts.map +0 -1
- package/dist/handlers.js +0 -55
- package/dist/provider.d.ts +0 -105
- package/dist/provider.d.ts.map +0 -1
- package/dist/provider.js +0 -160
- package/dist/serializers/account.d.ts +0 -164
- package/dist/serializers/account.d.ts.map +0 -1
- package/dist/serializers/account.js +0 -244
- package/dist/serializers/contract.d.ts +0 -62
- package/dist/serializers/contract.d.ts.map +0 -1
- package/dist/serializers/contract.js +0 -130
- package/dist/serializers/index.d.ts +0 -21
- package/dist/serializers/index.d.ts.map +0 -1
- package/dist/serializers/index.js +0 -154
- package/dist/serializers/log.d.ts +0 -66
- package/dist/serializers/log.d.ts.map +0 -1
- package/dist/serializers/log.js +0 -222
- package/dist/serializers/note.d.ts +0 -124
- package/dist/serializers/note.d.ts.map +0 -1
- package/dist/serializers/note.js +0 -208
- package/dist/serializers/transaction.d.ts +0 -99
- package/dist/serializers/transaction.d.ts.map +0 -1
- package/dist/serializers/transaction.js +0 -275
- package/dist/wallet.d.ts +0 -62
- package/dist/wallet.d.ts.map +0 -1
- package/dist/wallet.js +0 -77
- package/docs/classes/AztecChainProvider.md +0 -553
- package/docs/classes/AztecChainWallet.md +0 -409
- package/docs/classes/AztecProvider.md +0 -1112
- package/docs/interfaces/AztecWalletBaseMethodMap.md +0 -135
- package/docs/interfaces/AztecWalletEventMap.md +0 -17
- package/docs/type-aliases/AztecChainWalletMiddleware.md +0 -13
- package/docs/type-aliases/AztecWalletContext.md +0 -29
- package/docs/type-aliases/AztecWalletMethodHandler.md +0 -37
- package/docs/type-aliases/AztecWalletMiddleware.md +0 -13
- package/docs/type-aliases/AztecWalletRouterClient.md +0 -13
- package/docs/type-aliases/TransactionFunctionCall.md +0 -33
- package/docs/type-aliases/TransactionParams.md +0 -27
- package/src/aztecRemoteWallet.test.ts +0 -504
- package/src/aztecRemoteWallet.ts +0 -467
- package/src/chainProvider.test.ts +0 -401
- package/src/chainProvider.ts +0 -116
- package/src/handlers/aztecAccountWallet.test.ts +0 -649
- package/src/handlers/aztecAccountWallet.ts +0 -532
- package/src/handlers/transactions.ts +0 -124
- package/src/handlers.test.ts +0 -270
- package/src/handlers.ts +0 -70
- package/src/provider.test.ts +0 -274
- package/src/provider.ts +0 -189
- package/src/serializers/account.test.ts +0 -125
- package/src/serializers/account.ts +0 -301
- package/src/serializers/contract.test.ts +0 -24
- package/src/serializers/contract.ts +0 -183
- package/src/serializers/index.test.ts +0 -136
- package/src/serializers/index.ts +0 -191
- package/src/serializers/log.test.ts +0 -286
- package/src/serializers/log.ts +0 -292
- package/src/serializers/note.test.ts +0 -125
- package/src/serializers/note.ts +0 -250
- package/src/serializers/transaction.test.ts +0 -320
- package/src/serializers/transaction.ts +0 -409
- package/src/wallet.test.ts +0 -275
- package/src/wallet.ts +0 -94
@@ -0,0 +1,235 @@
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
2
|
+
import { AztecRouterProvider } from './aztec-router-provider.js';
|
3
|
+
import { AztecAddress, CompleteAddress } from '@aztec/aztec.js';
|
4
|
+
// import { jsonStringify } from '@aztec/foundation/json-rpc'; // Unused
|
5
|
+
import type {
|
6
|
+
JSONRPCRequest,
|
7
|
+
JSONRPCResponse,
|
8
|
+
JSONRPCTransport,
|
9
|
+
// JSONRPCMethodMap, // Unused
|
10
|
+
} from '@walletmesh/jsonrpc';
|
11
|
+
|
12
|
+
// Define a minimal method map for testing purposes
|
13
|
+
// import type { JSONRPCParams } from '@walletmesh/jsonrpc'; // Unused
|
14
|
+
|
15
|
+
// This map is for conceptual organization in tests; actual type safety comes from provider.call
|
16
|
+
// interface TestAztecMethodMap { // Unused
|
17
|
+
// aztec_getAddress: { result: AztecAddress; params?: undefined };
|
18
|
+
// aztec_getCompleteAddress: { result: CompleteAddress; params?: undefined };
|
19
|
+
// aztec_getBlockNumber: { result: number; params?: undefined };
|
20
|
+
// some_unknown_method: { result: unknown; params?: Record<string, unknown> };
|
21
|
+
// wm_call: { params: WmCallParams; result: unknown }; // This is the outer request
|
22
|
+
// wm_connect: {
|
23
|
+
// params?: { chainId?: string; options?: unknown };
|
24
|
+
// result: { sessionId: string; permissions: unknown };
|
25
|
+
// };
|
26
|
+
// }
|
27
|
+
|
28
|
+
// Define an interface for the structure of params for 'wm_call'
|
29
|
+
// Type aliases for simplified mock typing
|
30
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test mock simplification
|
31
|
+
type JSONRPCRequestAny = JSONRPCRequest<any, any, any>;
|
32
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test mock simplification
|
33
|
+
type JSONRPCResponseAny = JSONRPCResponse<any>;
|
34
|
+
interface WmCallParams {
|
35
|
+
chainId: string;
|
36
|
+
call: {
|
37
|
+
method: string;
|
38
|
+
params?: unknown;
|
39
|
+
};
|
40
|
+
}
|
41
|
+
describe('AztecRouterProvider', () => {
|
42
|
+
let mockTransport: JSONRPCTransport;
|
43
|
+
let provider: AztecRouterProvider;
|
44
|
+
let sendMock: ReturnType<typeof vi.fn>;
|
45
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test mock simplification
|
46
|
+
let messageHandler: (msg: JSONRPCResponse<any, any>) => void;
|
47
|
+
// let requestIdCounter = 0; // Unused
|
48
|
+
|
49
|
+
beforeEach(() => {
|
50
|
+
// requestIdCounter = 0; // Unused
|
51
|
+
sendMock = vi.fn();
|
52
|
+
mockTransport = {
|
53
|
+
send: sendMock,
|
54
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test mock simplification
|
55
|
+
onMessage: (handler: (msg: JSONRPCResponse<any, any>) => void) => {
|
56
|
+
messageHandler = handler;
|
57
|
+
// Return a dummy cleanup function as expected by onMessage
|
58
|
+
return () => {};
|
59
|
+
},
|
60
|
+
};
|
61
|
+
provider = new AztecRouterProvider(mockTransport);
|
62
|
+
});
|
63
|
+
|
64
|
+
describe('serialization', () => {
|
65
|
+
it('should serialize AztecAddress results correctly', async () => {
|
66
|
+
// Use a fixed test address string (AztecAddress format is 0x + 64 hex chars)
|
67
|
+
const testAddressStr = `0x${'1'.repeat(64)}`;
|
68
|
+
|
69
|
+
sendMock.mockImplementation(async (request: JSONRPCRequestAny) => {
|
70
|
+
if (request.method === 'wm_call') {
|
71
|
+
const resultPayload = {
|
72
|
+
serialized: JSON.stringify(testAddressStr),
|
73
|
+
method: 'aztec_getAddress',
|
74
|
+
};
|
75
|
+
const response: JSONRPCResponseAny = {
|
76
|
+
jsonrpc: '2.0',
|
77
|
+
id: request.id,
|
78
|
+
result: resultPayload,
|
79
|
+
};
|
80
|
+
process.nextTick(() => {
|
81
|
+
messageHandler(response);
|
82
|
+
});
|
83
|
+
}
|
84
|
+
});
|
85
|
+
|
86
|
+
provider['_sessionId'] = 'test-session';
|
87
|
+
|
88
|
+
const result = await provider.call('aztec:mainnet', {
|
89
|
+
method: 'aztec_getAddress',
|
90
|
+
});
|
91
|
+
|
92
|
+
expect(result).toBeDefined();
|
93
|
+
expect(result).toBeInstanceOf(AztecAddress);
|
94
|
+
expect((result as AztecAddress).toString()).toBe(testAddressStr);
|
95
|
+
});
|
96
|
+
|
97
|
+
it('should serialize CompleteAddress results correctly', async () => {
|
98
|
+
const testAddressStr =
|
99
|
+
'0x' +
|
100
|
+
'0637a51d0657242e85d8389b701d0b997a1e725beef42950eeb7b47ba900ceb2' +
|
101
|
+
'12b2a7eb642de67d21bace929313fb39ee45ffe7f5c413fb605c4e1cc71ede46' +
|
102
|
+
'270c635ac4debe508923847587d63f2004fdce3a0aadd4eb2fa32e756b3b7ceb' +
|
103
|
+
'100faf645e19c147761f6f18ef79be235c9b57a0005b3ecdda388f26fc20682b' +
|
104
|
+
'03f65a881a51f556be8f6d731d7d6f464c0a6cd04b6ce96ad03f968d9b254605' +
|
105
|
+
'040de5aed50467bbb4cd92d22b9d9fb84563606620b4c98be389a558345eadd3' +
|
106
|
+
'23db82d556864738e123fa48da67c8946bbea413b5d3b970c38ba908079812b3' +
|
107
|
+
'2880ba2fb9aa6d14696cba7dc3d8d39d1502e1ca459b0639793364fe97286432' +
|
108
|
+
'283df6e9a33d818fec833209ee18686cd306c49a7a479baefb725410bbaab16c' +
|
109
|
+
'303c5120ccdd027bfc35a7e666e6940d49c0dfcf25e56e3420558e42b6a3dd91';
|
110
|
+
|
111
|
+
sendMock.mockImplementation(async (request: JSONRPCRequestAny) => {
|
112
|
+
if (request.method === 'wm_call') {
|
113
|
+
const resultPayload = {
|
114
|
+
serialized: JSON.stringify(testAddressStr),
|
115
|
+
method: 'aztec_getCompleteAddress',
|
116
|
+
};
|
117
|
+
const response: JSONRPCResponseAny = {
|
118
|
+
jsonrpc: '2.0',
|
119
|
+
id: request.id,
|
120
|
+
result: resultPayload,
|
121
|
+
};
|
122
|
+
process.nextTick(() => {
|
123
|
+
messageHandler(response);
|
124
|
+
});
|
125
|
+
}
|
126
|
+
});
|
127
|
+
|
128
|
+
provider['_sessionId'] = 'test-session';
|
129
|
+
|
130
|
+
const result = await provider.call('aztec:mainnet', {
|
131
|
+
method: 'aztec_getCompleteAddress',
|
132
|
+
});
|
133
|
+
|
134
|
+
expect(result).toBeInstanceOf(CompleteAddress);
|
135
|
+
expect(result).toHaveProperty('address');
|
136
|
+
expect(result).toHaveProperty('publicKeys');
|
137
|
+
expect(result).toHaveProperty('partialAddress');
|
138
|
+
});
|
139
|
+
|
140
|
+
it('should pass through method calls without parameters', async () => {
|
141
|
+
let capturedRequest: JSONRPCRequestAny | undefined;
|
142
|
+
sendMock.mockImplementation(async (request: JSONRPCRequestAny) => {
|
143
|
+
if (request.method === 'wm_call') {
|
144
|
+
capturedRequest = request;
|
145
|
+
const response: JSONRPCResponseAny = {
|
146
|
+
jsonrpc: '2.0',
|
147
|
+
id: request.id,
|
148
|
+
result: 123,
|
149
|
+
};
|
150
|
+
process.nextTick(() => {
|
151
|
+
messageHandler(response);
|
152
|
+
});
|
153
|
+
}
|
154
|
+
});
|
155
|
+
|
156
|
+
provider['_sessionId'] = 'test-session';
|
157
|
+
|
158
|
+
await provider.call('aztec:mainnet', {
|
159
|
+
method: 'aztec_getBlockNumber',
|
160
|
+
});
|
161
|
+
|
162
|
+
expect(capturedRequest).toBeDefined();
|
163
|
+
if (capturedRequest) {
|
164
|
+
expect(capturedRequest.method).toBe('wm_call');
|
165
|
+
const params = capturedRequest.params as WmCallParams | undefined;
|
166
|
+
expect(params).toBeDefined();
|
167
|
+
|
168
|
+
if (params?.call) {
|
169
|
+
expect(params.call.method).toBe('aztec_getBlockNumber');
|
170
|
+
expect(params.call.params).toBeUndefined();
|
171
|
+
} else if (params) {
|
172
|
+
expect(params).toHaveProperty('call');
|
173
|
+
}
|
174
|
+
}
|
175
|
+
});
|
176
|
+
|
177
|
+
it('should handle methods without serializers', async () => {
|
178
|
+
sendMock.mockImplementation(async (request: JSONRPCRequestAny) => {
|
179
|
+
if (request.method === 'wm_call') {
|
180
|
+
const resultPayload = { someData: 'test' };
|
181
|
+
const response: JSONRPCResponseAny = {
|
182
|
+
jsonrpc: '2.0',
|
183
|
+
id: request.id,
|
184
|
+
result: resultPayload,
|
185
|
+
};
|
186
|
+
process.nextTick(() => {
|
187
|
+
messageHandler(response);
|
188
|
+
});
|
189
|
+
}
|
190
|
+
});
|
191
|
+
|
192
|
+
provider['_sessionId'] = 'test-session';
|
193
|
+
|
194
|
+
const result = await provider.call('aztec:mainnet', {
|
195
|
+
method: 'some_unknown_method' as string,
|
196
|
+
params: { test: 'data' },
|
197
|
+
});
|
198
|
+
|
199
|
+
expect(result).toEqual({ someData: 'test' });
|
200
|
+
});
|
201
|
+
});
|
202
|
+
|
203
|
+
describe('connection management', () => {
|
204
|
+
it('should maintain session after connection', async () => {
|
205
|
+
sendMock.mockImplementation(async (request: JSONRPCRequestAny) => {
|
206
|
+
if (request.method === 'wm_connect') {
|
207
|
+
const resultPayload = {
|
208
|
+
sessionId: 'test-session-123',
|
209
|
+
permissions: {
|
210
|
+
'aztec:mainnet': {
|
211
|
+
methods: ['aztec_getAddress'],
|
212
|
+
metadata: {},
|
213
|
+
},
|
214
|
+
},
|
215
|
+
};
|
216
|
+
const response: JSONRPCResponseAny = {
|
217
|
+
jsonrpc: '2.0',
|
218
|
+
id: request.id,
|
219
|
+
result: resultPayload,
|
220
|
+
};
|
221
|
+
process.nextTick(() => {
|
222
|
+
messageHandler(response);
|
223
|
+
});
|
224
|
+
}
|
225
|
+
});
|
226
|
+
|
227
|
+
const result = await provider.connect({
|
228
|
+
'aztec:mainnet': ['aztec_getAddress'],
|
229
|
+
});
|
230
|
+
|
231
|
+
expect(result.sessionId).toBe('test-session-123');
|
232
|
+
expect(provider.sessionId).toBe('test-session-123');
|
233
|
+
});
|
234
|
+
});
|
235
|
+
});
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import { WalletRouterProvider } from '@walletmesh/router';
|
2
|
+
import type { JSONRPCTransport } from '@walletmesh/jsonrpc';
|
3
|
+
import { registerAztecSerializers } from './register-serializers.js';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* An extended {@link WalletRouterProvider} specifically for Aztec network interactions.
|
7
|
+
*
|
8
|
+
* This class automatically registers all necessary Aztec-specific type serializers
|
9
|
+
* (e.g., for `AztecAddress`, `Fr`, `TxExecutionRequest`) upon instantiation.
|
10
|
+
* This ensures that when dApps communicate with an Aztec wallet via this provider,
|
11
|
+
* all Aztec types are correctly serialized for JSON-RPC transport and deserialized
|
12
|
+
* back into their proper object instances on receipt.
|
13
|
+
*
|
14
|
+
* It simplifies the setup for dApp developers, as they do not need to manually
|
15
|
+
* register serializers for Aztec types.
|
16
|
+
*
|
17
|
+
* @example
|
18
|
+
* ```typescript
|
19
|
+
* import { AztecRouterProvider, createAztecWallet } from '@walletmesh/aztec-rpc-wallet';
|
20
|
+
* import { MyCustomTransport } from './my-custom-transport'; // Assuming a custom transport
|
21
|
+
*
|
22
|
+
* // 1. Create a JSON-RPC transport
|
23
|
+
* const transport = new MyCustomTransport();
|
24
|
+
*
|
25
|
+
* // 2. Create the AztecRouterProvider instance
|
26
|
+
* const provider = new AztecRouterProvider(transport);
|
27
|
+
*
|
28
|
+
* // 3. Connect to the Aztec chain (e.g., testnet) and request permissions
|
29
|
+
* await provider.connect({
|
30
|
+
* 'aztec:testnet': ['aztec_getAddress', 'aztec_sendTx']
|
31
|
+
* });
|
32
|
+
*
|
33
|
+
* // 4. Create an AztecDappWallet instance using the provider
|
34
|
+
* const wallet = await createAztecWallet(provider, 'aztec:testnet');
|
35
|
+
*
|
36
|
+
* // Now, calls made through 'wallet' will automatically handle Aztec type serialization:
|
37
|
+
* const address = await wallet.getAddress(); // AztecAddress instance
|
38
|
+
* // const txRequest = ...;
|
39
|
+
* // const txHash = await wallet.sendTx(await wallet.proveTx(txRequest)); // Tx, TxHash instances
|
40
|
+
* ```
|
41
|
+
*
|
42
|
+
* @see {@link WalletRouterProvider} for the base class functionality.
|
43
|
+
* @see {@link registerAztecSerializers} for the underlying serializer registration.
|
44
|
+
* @see {@link AztecDappWallet} which is typically used with this provider.
|
45
|
+
*/
|
46
|
+
export class AztecRouterProvider extends WalletRouterProvider {
|
47
|
+
/**
|
48
|
+
* Constructs an instance of `AztecRouterProvider`.
|
49
|
+
*
|
50
|
+
* Upon construction, it immediately registers all Aztec-specific serializers
|
51
|
+
* with the underlying JSON-RPC node managed by the `WalletRouterProvider`.
|
52
|
+
*
|
53
|
+
* @param transport - The {@link JSONRPCTransport} instance to be used for
|
54
|
+
* communication between the dApp and the WalletRouter.
|
55
|
+
* @param context - Optional context object that can be passed to the
|
56
|
+
* `WalletRouterProvider` constructor.
|
57
|
+
*/
|
58
|
+
constructor(transport: JSONRPCTransport, context?: Record<string, unknown>) {
|
59
|
+
super(transport, context);
|
60
|
+
|
61
|
+
// Register all Aztec serializers on this provider instance
|
62
|
+
registerAztecSerializers(this);
|
63
|
+
}
|
64
|
+
}
|
@@ -0,0 +1,187 @@
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
2
|
+
import { ALL_AZTEC_METHODS, connectAztec } from './helpers.js';
|
3
|
+
import { createAztecWallet, type AztecDappWallet } from './aztec-dapp-wallet.js';
|
4
|
+
import type { WalletRouterProvider } from '@walletmesh/router'; // Removed SessionData
|
5
|
+
|
6
|
+
// Mock the aztec-dapp-wallet module
|
7
|
+
vi.mock('./aztec-dapp-wallet.js', () => ({
|
8
|
+
createAztecWallet: vi.fn(),
|
9
|
+
}));
|
10
|
+
|
11
|
+
// Mock provider
|
12
|
+
const createMockProvider = () => {
|
13
|
+
const connect = vi.fn();
|
14
|
+
const on = vi.fn();
|
15
|
+
const off = vi.fn();
|
16
|
+
const call = vi.fn();
|
17
|
+
return {
|
18
|
+
connect,
|
19
|
+
on,
|
20
|
+
off,
|
21
|
+
call,
|
22
|
+
} as unknown as WalletRouterProvider;
|
23
|
+
};
|
24
|
+
|
25
|
+
describe('helpers', () => {
|
26
|
+
let provider: ReturnType<typeof createMockProvider>;
|
27
|
+
|
28
|
+
beforeEach(() => {
|
29
|
+
provider = createMockProvider();
|
30
|
+
vi.clearAllMocks();
|
31
|
+
});
|
32
|
+
|
33
|
+
describe('constants', () => {
|
34
|
+
it('should export ALL_AZTEC_METHODS with all available methods', () => {
|
35
|
+
// Should include essential methods
|
36
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getAddress');
|
37
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getCompleteAddress');
|
38
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getChainId');
|
39
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getVersion');
|
40
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_sendTx');
|
41
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getTxReceipt');
|
42
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_simulateTx');
|
43
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getNodeInfo');
|
44
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getBlockNumber');
|
45
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getCurrentBaseFees');
|
46
|
+
|
47
|
+
// Should include additional methods
|
48
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_registerSender');
|
49
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getSenders');
|
50
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_removeSender');
|
51
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_registerContract');
|
52
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_registerContractClass');
|
53
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getContractMetadata');
|
54
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getContractClassMetadata');
|
55
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_proveTx');
|
56
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_profileTx');
|
57
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_simulateUtility');
|
58
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getPrivateEvents');
|
59
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getPublicEvents');
|
60
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getPXEInfo');
|
61
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_getBlock');
|
62
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_createAuthWit');
|
63
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_wmDeployContract');
|
64
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_wmExecuteTx');
|
65
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_wmSimulateTx');
|
66
|
+
expect(ALL_AZTEC_METHODS).toContain('aztec_wmDeployContract');
|
67
|
+
|
68
|
+
// Check total length
|
69
|
+
expect(ALL_AZTEC_METHODS).toHaveLength(28);
|
70
|
+
});
|
71
|
+
});
|
72
|
+
|
73
|
+
describe('connectAztec', () => {
|
74
|
+
it('should connect and create wallet with default chainId and methods', async () => {
|
75
|
+
const mockConnectResult = {
|
76
|
+
sessionId: 'test-session',
|
77
|
+
permissions: { 'aztec:mainnet': {} }, // Mock HumanReadableChainPermissions
|
78
|
+
};
|
79
|
+
const mockWallet = { address: '0x123' } as unknown as AztecDappWallet;
|
80
|
+
|
81
|
+
vi.mocked(provider.connect).mockResolvedValue(mockConnectResult);
|
82
|
+
vi.mocked(createAztecWallet).mockResolvedValue(mockWallet);
|
83
|
+
|
84
|
+
const result = await connectAztec(provider);
|
85
|
+
|
86
|
+
expect(provider.connect).toHaveBeenCalledWith({
|
87
|
+
'aztec:mainnet': ALL_AZTEC_METHODS,
|
88
|
+
});
|
89
|
+
expect(createAztecWallet).toHaveBeenCalledWith(provider, 'aztec:mainnet');
|
90
|
+
expect(result).toEqual({
|
91
|
+
sessionId: mockConnectResult.sessionId,
|
92
|
+
wallet: mockWallet,
|
93
|
+
});
|
94
|
+
});
|
95
|
+
|
96
|
+
it('should connect and create wallet with custom chainId', async () => {
|
97
|
+
const mockConnectResult = {
|
98
|
+
sessionId: 'test-session',
|
99
|
+
permissions: { 'aztec:custom': {} },
|
100
|
+
};
|
101
|
+
const mockWallet = { address: '0x123' } as unknown as AztecDappWallet;
|
102
|
+
const customChainId = 'aztec:custom';
|
103
|
+
|
104
|
+
vi.mocked(provider.connect).mockResolvedValue(mockConnectResult);
|
105
|
+
vi.mocked(createAztecWallet).mockResolvedValue(mockWallet);
|
106
|
+
|
107
|
+
const result = await connectAztec(provider, customChainId);
|
108
|
+
|
109
|
+
expect(provider.connect).toHaveBeenCalledWith({
|
110
|
+
[customChainId]: ALL_AZTEC_METHODS,
|
111
|
+
});
|
112
|
+
expect(createAztecWallet).toHaveBeenCalledWith(provider, customChainId);
|
113
|
+
expect(result).toEqual({
|
114
|
+
sessionId: mockConnectResult.sessionId,
|
115
|
+
wallet: mockWallet,
|
116
|
+
});
|
117
|
+
});
|
118
|
+
|
119
|
+
it('should connect and create wallet with custom methods', async () => {
|
120
|
+
const mockConnectResult = {
|
121
|
+
sessionId: 'test-session',
|
122
|
+
permissions: { 'aztec:mainnet': {} },
|
123
|
+
};
|
124
|
+
const mockWallet = { address: '0x123' } as unknown as AztecDappWallet;
|
125
|
+
const customMethods = ['aztec_getAddress', 'aztec_sendTx'];
|
126
|
+
|
127
|
+
vi.mocked(provider.connect).mockResolvedValue(mockConnectResult);
|
128
|
+
vi.mocked(createAztecWallet).mockResolvedValue(mockWallet);
|
129
|
+
|
130
|
+
const result = await connectAztec(provider, undefined, customMethods);
|
131
|
+
|
132
|
+
expect(provider.connect).toHaveBeenCalledWith({
|
133
|
+
'aztec:mainnet': customMethods,
|
134
|
+
});
|
135
|
+
expect(createAztecWallet).toHaveBeenCalledWith(provider, 'aztec:mainnet');
|
136
|
+
expect(result).toEqual({
|
137
|
+
sessionId: mockConnectResult.sessionId,
|
138
|
+
wallet: mockWallet,
|
139
|
+
});
|
140
|
+
});
|
141
|
+
|
142
|
+
it('should connect and create wallet with custom chainId and methods', async () => {
|
143
|
+
const mockConnectResult = {
|
144
|
+
sessionId: 'test-session',
|
145
|
+
permissions: { 'aztec:testnet': {} },
|
146
|
+
};
|
147
|
+
const mockWallet = { address: '0x123' } as unknown as AztecDappWallet;
|
148
|
+
const customChainId = 'aztec:testnet';
|
149
|
+
const customMethods = ['aztec_getAddress'];
|
150
|
+
|
151
|
+
vi.mocked(provider.connect).mockResolvedValue(mockConnectResult);
|
152
|
+
vi.mocked(createAztecWallet).mockResolvedValue(mockWallet);
|
153
|
+
|
154
|
+
const result = await connectAztec(provider, customChainId, customMethods);
|
155
|
+
|
156
|
+
expect(provider.connect).toHaveBeenCalledWith({
|
157
|
+
[customChainId]: customMethods,
|
158
|
+
});
|
159
|
+
expect(createAztecWallet).toHaveBeenCalledWith(provider, customChainId);
|
160
|
+
expect(result).toEqual({
|
161
|
+
sessionId: mockConnectResult.sessionId,
|
162
|
+
wallet: mockWallet,
|
163
|
+
});
|
164
|
+
});
|
165
|
+
|
166
|
+
it('should handle connection failure', async () => {
|
167
|
+
const error = new Error('Connection failed');
|
168
|
+
vi.mocked(provider.connect).mockRejectedValue(error);
|
169
|
+
|
170
|
+
await expect(connectAztec(provider)).rejects.toThrow('Connection failed');
|
171
|
+
expect(createAztecWallet).not.toHaveBeenCalled();
|
172
|
+
});
|
173
|
+
|
174
|
+
it('should handle wallet creation failure', async () => {
|
175
|
+
const mockConnectResult = {
|
176
|
+
sessionId: 'test-session',
|
177
|
+
permissions: { 'aztec:mainnet': {} },
|
178
|
+
};
|
179
|
+
const error = new Error('Wallet creation failed');
|
180
|
+
|
181
|
+
vi.mocked(provider.connect).mockResolvedValue(mockConnectResult);
|
182
|
+
vi.mocked(createAztecWallet).mockRejectedValue(error);
|
183
|
+
|
184
|
+
await expect(connectAztec(provider)).rejects.toThrow('Wallet creation failed');
|
185
|
+
});
|
186
|
+
});
|
187
|
+
});
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import type { AztecChainId } from '../types.js';
|
2
|
+
import type { AztecDappWallet } from './aztec-dapp-wallet.js';
|
3
|
+
import { createAztecWallet } from './aztec-dapp-wallet.js';
|
4
|
+
import type { AztecRouterProvider } from './aztec-router-provider.js';
|
5
|
+
import { createLogger } from '@aztec/foundation/log';
|
6
|
+
|
7
|
+
const logger = createLogger('aztec-rpc-wallet:helpers');
|
8
|
+
|
9
|
+
/**
|
10
|
+
* A comprehensive list of all JSON-RPC methods supported by the Aztec RPC wallet.
|
11
|
+
* This array includes standard Aztec wallet methods as well as WalletMesh-specific extensions (prefixed with `wm_`).
|
12
|
+
* It can be used when establishing a connection to request permissions for all available functionalities.
|
13
|
+
*
|
14
|
+
* @see {@link AztecWalletMethodMap} for detailed type information on each method.
|
15
|
+
* @see {@link connectAztec} and {@link connectAztecWithWallet} which use this list by default.
|
16
|
+
*/
|
17
|
+
export const ALL_AZTEC_METHODS = [
|
18
|
+
'aztec_getAddress',
|
19
|
+
'aztec_getCompleteAddress',
|
20
|
+
'aztec_getChainId',
|
21
|
+
'aztec_getVersion',
|
22
|
+
'aztec_sendTx',
|
23
|
+
'aztec_getTxReceipt',
|
24
|
+
'aztec_simulateTx',
|
25
|
+
'aztec_getNodeInfo',
|
26
|
+
'aztec_getBlockNumber',
|
27
|
+
'aztec_getCurrentBaseFees',
|
28
|
+
'aztec_registerSender',
|
29
|
+
'aztec_getSenders',
|
30
|
+
'aztec_removeSender',
|
31
|
+
'aztec_registerContract',
|
32
|
+
'aztec_registerContractClass',
|
33
|
+
'aztec_getContractMetadata',
|
34
|
+
'aztec_getContractClassMetadata',
|
35
|
+
'aztec_proveTx',
|
36
|
+
'aztec_profileTx',
|
37
|
+
'aztec_simulateUtility',
|
38
|
+
'aztec_getPrivateEvents',
|
39
|
+
'aztec_getPublicEvents',
|
40
|
+
'aztec_getPXEInfo',
|
41
|
+
'aztec_getBlock',
|
42
|
+
'aztec_createAuthWit',
|
43
|
+
'aztec_wmDeployContract',
|
44
|
+
'aztec_wmExecuteTx',
|
45
|
+
'aztec_wmSimulateTx',
|
46
|
+
] as const;
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Establishes a connection to an Aztec wallet service and creates an initialized {@link AztecDappWallet} instance.
|
50
|
+
* This function requests permissions for the specified methods on the given Aztec chain,
|
51
|
+
* then instantiates and initializes the wallet.
|
52
|
+
* Initialization includes fetching and caching essential data like the wallet address and chain ID.
|
53
|
+
*
|
54
|
+
* By default, it requests permissions for all methods defined in {@link ALL_AZTEC_METHODS}
|
55
|
+
* on the 'aztec:mainnet' chain.
|
56
|
+
*
|
57
|
+
* @param provider - The {@link AztecRouterProvider} instance to use for the connection.
|
58
|
+
* This provider must be configured with appropriate transport and Aztec serializers.
|
59
|
+
* @param chainId - The {@link AztecChainId} to connect to (e.g., 'aztec:mainnet', 'aztec:31337').
|
60
|
+
* Defaults to 'aztec:mainnet'.
|
61
|
+
* @param methods - An array of method names for which permissions are requested.
|
62
|
+
* Defaults to {@link ALL_AZTEC_METHODS}.
|
63
|
+
* @returns A promise that resolves to an object containing the `sessionId` for the connection
|
64
|
+
* and a fully initialized {@link AztecDappWallet} instance.
|
65
|
+
* @throws If the connection or wallet initialization fails.
|
66
|
+
*
|
67
|
+
* @example
|
68
|
+
* ```typescript
|
69
|
+
* const provider = new AztecRouterProvider(myTransport);
|
70
|
+
* const { sessionId, wallet } = await connectAztec(provider, 'aztec:testnet');
|
71
|
+
* const address = wallet.getAddress(); // Wallet is ready to use
|
72
|
+
* console.log('Connected with session ID:', sessionId, 'Wallet address:', address.toString());
|
73
|
+
* ```
|
74
|
+
*/
|
75
|
+
export async function connectAztec(
|
76
|
+
provider: AztecRouterProvider,
|
77
|
+
chainId: AztecChainId = 'aztec:mainnet',
|
78
|
+
methods: readonly (keyof import('../types.js').AztecWalletMethodMap | string)[] = ALL_AZTEC_METHODS,
|
79
|
+
): Promise<{ sessionId: string; wallet: AztecDappWallet }> {
|
80
|
+
// Establish connection
|
81
|
+
const { sessionId } = await provider.connect({
|
82
|
+
[chainId]: [...methods] as string[],
|
83
|
+
});
|
84
|
+
|
85
|
+
// Create and initialize wallet (this pre-caches all synchronous values)
|
86
|
+
const wallet = await createAztecWallet(provider, chainId);
|
87
|
+
logger.debug(`Connected to Aztec wallet: ${sessionId}`);
|
88
|
+
|
89
|
+
return { sessionId, wallet };
|
90
|
+
}
|
91
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import type { WalletRouterProvider } from '@walletmesh/router';
|
2
|
+
import { AztecWalletSerializer } from '../wallet/serializers.js';
|
3
|
+
import type { AztecWalletMethodMap } from '../types.js';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Registers the {@link AztecWalletSerializer} for all relevant Aztec JSON-RPC methods
|
7
|
+
* on a given {@link WalletRouterProvider} instance.
|
8
|
+
*
|
9
|
+
* This utility function ensures that any `WalletRouterProvider` (not just the specialized
|
10
|
+
* {@link AztecRouterProvider}) can be configured to correctly handle serialization and
|
11
|
+
* deserialization of Aztec-specific data types (e.g., `AztecAddress`, `Fr`, `TxExecutionRequest`)
|
12
|
+
* when interacting with an Aztec wallet.
|
13
|
+
*
|
14
|
+
* It iterates through a predefined list of Aztec methods and associates them with
|
15
|
+
* the comprehensive {@link AztecWalletSerializer}.
|
16
|
+
*
|
17
|
+
* @param provider - The {@link WalletRouterProvider} instance on which to register the serializers.
|
18
|
+
* After this function call, the provider will be equipped to handle Aztec methods.
|
19
|
+
*
|
20
|
+
* @example
|
21
|
+
* ```typescript
|
22
|
+
* import { WalletRouterProvider } from '@walletmesh/router';
|
23
|
+
* import { registerAztecSerializers, ALL_AZTEC_METHODS } from '@walletmesh/aztec-rpc-wallet';
|
24
|
+
* import { MyCustomTransport } from './my-custom-transport';
|
25
|
+
*
|
26
|
+
* // 1. Create a generic WalletRouterProvider
|
27
|
+
* const transport = new MyCustomTransport();
|
28
|
+
* const provider = new WalletRouterProvider(transport);
|
29
|
+
*
|
30
|
+
* // 2. Register Aztec serializers on it
|
31
|
+
* registerAztecSerializers(provider);
|
32
|
+
*
|
33
|
+
* // 3. Now the provider can correctly handle Aztec methods and types
|
34
|
+
* await provider.connect({ 'aztec:testnet': ALL_AZTEC_METHODS });
|
35
|
+
* const address = await provider.call('aztec:testnet', { method: 'aztec_getAddress' });
|
36
|
+
* // 'address' will be correctly deserialized into an AztecAddress instance (or equivalent based on serializer)
|
37
|
+
* ```
|
38
|
+
*
|
39
|
+
* @see {@link AztecWalletSerializer} for the actual serialization logic.
|
40
|
+
* @see {@link AztecRouterProvider} for a provider that calls this automatically.
|
41
|
+
* @see {@link AztecWalletMethodMap} for the list of methods and their types.
|
42
|
+
*/
|
43
|
+
export function registerAztecSerializers(provider: WalletRouterProvider): void {
|
44
|
+
// List of all Aztec wallet methods that need the AztecWalletSerializer.
|
45
|
+
// This list should ideally be kept in sync with ALL_AZTEC_METHODS or derived from AztecWalletMethodMap.
|
46
|
+
// Note: 'aztec_createTxExecutionRequest' is a client-side method, not a direct RPC method name.
|
47
|
+
// The actual RPC methods involved in tx creation are typically aztec_proveTx, aztec_sendTx, etc.
|
48
|
+
// However, if it were an RPC method, it would be listed here.
|
49
|
+
// For now, using a manually curated list that matches most of AztecWalletMethodMap.
|
50
|
+
const aztecMethods: (keyof AztecWalletMethodMap)[] = [
|
51
|
+
// Chain/Node Methods
|
52
|
+
'aztec_getBlock',
|
53
|
+
'aztec_getBlockNumber',
|
54
|
+
'aztec_getChainId',
|
55
|
+
'aztec_getVersion',
|
56
|
+
'aztec_getNodeInfo',
|
57
|
+
'aztec_getProvenBlockNumber',
|
58
|
+
'aztec_getPXEInfo',
|
59
|
+
'aztec_getCurrentBaseFees',
|
60
|
+
|
61
|
+
// Account Methods
|
62
|
+
'aztec_getAddress',
|
63
|
+
'aztec_getCompleteAddress',
|
64
|
+
|
65
|
+
// AuthWitness Methods
|
66
|
+
'aztec_createAuthWit',
|
67
|
+
|
68
|
+
// Sender Methods
|
69
|
+
'aztec_registerSender',
|
70
|
+
'aztec_getSenders',
|
71
|
+
'aztec_removeSender',
|
72
|
+
|
73
|
+
// Contract Methods
|
74
|
+
'aztec_getContracts',
|
75
|
+
'aztec_getContractMetadata',
|
76
|
+
'aztec_getContractClassMetadata',
|
77
|
+
'aztec_registerContract',
|
78
|
+
'aztec_registerContractClass',
|
79
|
+
|
80
|
+
// Transaction Methods
|
81
|
+
// 'aztec_createTxExecutionRequest', // This is a client-side method, not an RPC method.
|
82
|
+
'aztec_proveTx',
|
83
|
+
'aztec_sendTx',
|
84
|
+
'aztec_getTxReceipt',
|
85
|
+
'aztec_simulateTx',
|
86
|
+
'aztec_profileTx',
|
87
|
+
'aztec_simulateUtility',
|
88
|
+
|
89
|
+
// Event Methods
|
90
|
+
'aztec_getPrivateEvents',
|
91
|
+
'aztec_getPublicEvents',
|
92
|
+
|
93
|
+
// Contract Interaction Methods (WalletMesh specific)
|
94
|
+
'aztec_wmExecuteTx',
|
95
|
+
'aztec_wmSimulateTx',
|
96
|
+
'aztec_wmDeployContract',
|
97
|
+
|
98
|
+
// Base WalletMesh method often included
|
99
|
+
'wm_getSupportedMethods',
|
100
|
+
];
|
101
|
+
|
102
|
+
// Register the unified Aztec serializer for all methods
|
103
|
+
for (const method of aztecMethods) {
|
104
|
+
// The AztecWalletSerializer is already set up with all the proper
|
105
|
+
// serialization logic for each method
|
106
|
+
provider.registerMethodSerializer(method as string, AztecWalletSerializer);
|
107
|
+
}
|
108
|
+
}
|