@beclab/olaresid 0.1.13 → 0.2.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 (181) hide show
  1. package/CLI-TREE.md +107 -0
  2. package/CLI.md +122 -1340
  3. package/README.md +30 -12
  4. package/SDK-TREE.md +151 -0
  5. package/TAG.md +95 -41
  6. package/config.json +6 -4
  7. package/dist/abi/TerminusDIDQueryABI.d.ts +397 -0
  8. package/dist/abi/TerminusDIDQueryABI.d.ts.map +1 -0
  9. package/dist/abi/TerminusDIDQueryABI.js +519 -0
  10. package/dist/abi/TerminusDIDQueryABI.js.map +1 -0
  11. package/dist/business/index.d.ts.map +1 -1
  12. package/dist/business/index.js +9 -23
  13. package/dist/business/index.js.map +1 -1
  14. package/dist/business/tag-context.d.ts +1 -0
  15. package/dist/business/tag-context.d.ts.map +1 -1
  16. package/dist/business/tag-context.js +13 -7
  17. package/dist/business/tag-context.js.map +1 -1
  18. package/dist/cli.js +177 -76
  19. package/dist/cli.js.map +1 -1
  20. package/dist/config/index.d.ts +16 -4
  21. package/dist/config/index.d.ts.map +1 -1
  22. package/dist/config/index.js +28 -14
  23. package/dist/config/index.js.map +1 -1
  24. package/dist/domain/core.d.ts +65 -0
  25. package/dist/domain/core.d.ts.map +1 -0
  26. package/dist/domain/core.js +317 -0
  27. package/dist/domain/core.js.map +1 -0
  28. package/dist/domain/index.d.ts +104 -57
  29. package/dist/domain/index.d.ts.map +1 -1
  30. package/dist/domain/index.js +188 -428
  31. package/dist/domain/index.js.map +1 -1
  32. package/dist/domain/types.d.ts +56 -0
  33. package/dist/domain/types.d.ts.map +1 -0
  34. package/dist/domain/types.js +3 -0
  35. package/dist/domain/types.js.map +1 -0
  36. package/dist/index.d.ts +80 -23
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +152 -143
  39. package/dist/index.js.map +1 -1
  40. package/dist/utils/crypto-utils.d.ts +110 -0
  41. package/dist/utils/crypto-utils.d.ts.map +1 -1
  42. package/dist/utils/crypto-utils.js +127 -8
  43. package/dist/utils/crypto-utils.js.map +1 -1
  44. package/dist/utils/error-parser.d.ts.map +1 -1
  45. package/dist/utils/error-parser.js +2 -1
  46. package/dist/utils/error-parser.js.map +1 -1
  47. package/dist/utils/event-parser.d.ts +161 -0
  48. package/dist/utils/event-parser.d.ts.map +1 -0
  49. package/dist/utils/event-parser.js +140 -0
  50. package/dist/utils/event-parser.js.map +1 -0
  51. package/dist/utils/tag-type-builder.d.ts +43 -0
  52. package/dist/utils/tag-type-builder.d.ts.map +1 -1
  53. package/dist/utils/tag-type-builder.js +122 -0
  54. package/dist/utils/tag-type-builder.js.map +1 -1
  55. package/dist/utils/tag-type-parser.d.ts +70 -0
  56. package/dist/utils/tag-type-parser.d.ts.map +1 -0
  57. package/dist/utils/tag-type-parser.js +190 -0
  58. package/dist/utils/tag-type-parser.js.map +1 -0
  59. package/examples/create-with-rpc-demo.ts +142 -0
  60. package/examples/fetch-all-flat-demo.ts +159 -0
  61. package/examples/fetch-by-indices-demo.ts +235 -0
  62. package/examples/fetch-domain-demo.ts +137 -0
  63. package/examples/fetch-domains-demo.ts +221 -0
  64. package/examples/frontend-demo/index.html +2 -2
  65. package/examples/frontend-demo/package-lock.json +4 -1
  66. package/examples/index.ts +3 -5
  67. package/jest.config.js +25 -0
  68. package/package.json +6 -2
  69. package/src/abi/TerminusDIDQueryABI.ts +516 -0
  70. package/src/business/index.ts +9 -33
  71. package/src/business/tag-context.ts +35 -7
  72. package/src/cli.ts +253 -90
  73. package/src/config/index.ts +34 -19
  74. package/src/domain/core.ts +382 -0
  75. package/src/domain/index.ts +271 -641
  76. package/src/domain/types.ts +59 -0
  77. package/src/index.ts +221 -207
  78. package/src/utils/crypto-utils.ts +205 -2
  79. package/src/utils/error-parser.ts +2 -1
  80. package/src/utils/event-parser.ts +353 -0
  81. package/src/utils/tag-type-builder.ts +138 -0
  82. package/src/utils/tag-type-parser.ts +246 -0
  83. package/tests/unit/crypto-utils.test.ts +338 -0
  84. package/tests/unit/ed25519-jwk.test.ts +201 -0
  85. package/tests/unit/event-parser.test.ts +690 -0
  86. package/tests/unit/generate-mnemonic.test.ts +268 -0
  87. package/tests/unit/olares-id-format.test.ts +321 -0
  88. package/tests/unit/tag-type-parser.test.ts +802 -0
  89. package/tests/unit/tag-types.test.ts +821 -0
  90. package/tsconfig.json +3 -2
  91. package/dist/abi/ABITypeABI.d.ts +0 -88
  92. package/dist/abi/ABITypeABI.d.ts.map +0 -1
  93. package/dist/abi/ABITypeABI.js +0 -382
  94. package/dist/abi/ABITypeABI.js.map +0 -1
  95. package/dist/abi/RegistryABI.d.ts +0 -77
  96. package/dist/abi/RegistryABI.d.ts.map +0 -1
  97. package/dist/abi/RegistryABI.js +0 -462
  98. package/dist/abi/RegistryABI.js.map +0 -1
  99. package/dist/tag/address.d.ts +0 -11
  100. package/dist/tag/address.d.ts.map +0 -1
  101. package/dist/tag/address.js +0 -44
  102. package/dist/tag/address.js.map +0 -1
  103. package/dist/tag/array.d.ts +0 -14
  104. package/dist/tag/array.d.ts.map +0 -1
  105. package/dist/tag/array.js +0 -72
  106. package/dist/tag/array.js.map +0 -1
  107. package/dist/tag/bool.d.ts +0 -11
  108. package/dist/tag/bool.d.ts.map +0 -1
  109. package/dist/tag/bool.js +0 -43
  110. package/dist/tag/bool.js.map +0 -1
  111. package/dist/tag/bytes.d.ts +0 -11
  112. package/dist/tag/bytes.d.ts.map +0 -1
  113. package/dist/tag/bytes.js +0 -37
  114. package/dist/tag/bytes.js.map +0 -1
  115. package/dist/tag/flarray.d.ts +0 -15
  116. package/dist/tag/flarray.d.ts.map +0 -1
  117. package/dist/tag/flarray.js +0 -81
  118. package/dist/tag/flarray.js.map +0 -1
  119. package/dist/tag/flbytes.d.ts +0 -11
  120. package/dist/tag/flbytes.d.ts.map +0 -1
  121. package/dist/tag/flbytes.js +0 -47
  122. package/dist/tag/flbytes.js.map +0 -1
  123. package/dist/tag/index.d.ts +0 -32
  124. package/dist/tag/index.d.ts.map +0 -1
  125. package/dist/tag/index.js +0 -121
  126. package/dist/tag/index.js.map +0 -1
  127. package/dist/tag/int.d.ts +0 -12
  128. package/dist/tag/int.d.ts.map +0 -1
  129. package/dist/tag/int.js +0 -49
  130. package/dist/tag/int.js.map +0 -1
  131. package/dist/tag/string.d.ts +0 -11
  132. package/dist/tag/string.d.ts.map +0 -1
  133. package/dist/tag/string.js +0 -37
  134. package/dist/tag/string.js.map +0 -1
  135. package/dist/tag/tag.d.ts +0 -67
  136. package/dist/tag/tag.d.ts.map +0 -1
  137. package/dist/tag/tag.js +0 -157
  138. package/dist/tag/tag.js.map +0 -1
  139. package/dist/tag/tuple.d.ts +0 -17
  140. package/dist/tag/tuple.d.ts.map +0 -1
  141. package/dist/tag/tuple.js +0 -162
  142. package/dist/tag/tuple.js.map +0 -1
  143. package/dist/tag/uint.d.ts +0 -12
  144. package/dist/tag/uint.d.ts.map +0 -1
  145. package/dist/tag/uint.js +0 -49
  146. package/dist/tag/uint.js.map +0 -1
  147. package/dist/test/did.d.ts +0 -2
  148. package/dist/test/did.d.ts.map +0 -1
  149. package/dist/test/did.js +0 -177
  150. package/dist/test/did.js.map +0 -1
  151. package/dist/utils/tag-abi-codec.d.ts +0 -69
  152. package/dist/utils/tag-abi-codec.d.ts.map +0 -1
  153. package/dist/utils/tag-abi-codec.js +0 -144
  154. package/dist/utils/tag-abi-codec.js.map +0 -1
  155. package/examples/crypto-utilities.ts +0 -140
  156. package/examples/ed25519-jwk.ts +0 -73
  157. package/examples/generate-mnemonic.ts +0 -149
  158. package/examples/legacy.ts +0 -33
  159. package/examples/olares-id-format.ts +0 -197
  160. package/examples/tag-builder.ts +0 -235
  161. package/examples/tag-nested-tuple.ts +0 -190
  162. package/examples/tag-simple.ts +0 -149
  163. package/examples/tag-tagger.ts +0 -217
  164. package/examples/test-nested-tuple-conversion.ts +0 -143
  165. package/examples/test-type-bytes-parser.ts +0 -70
  166. package/src/abi/ABITypeABI.ts +0 -379
  167. package/src/abi/RegistryABI.ts +0 -459
  168. package/src/tag/address.ts +0 -48
  169. package/src/tag/array.ts +0 -80
  170. package/src/tag/bool.ts +0 -43
  171. package/src/tag/bytes.ts +0 -38
  172. package/src/tag/flarray.ts +0 -99
  173. package/src/tag/flbytes.ts +0 -48
  174. package/src/tag/index.ts +0 -170
  175. package/src/tag/int.ts +0 -51
  176. package/src/tag/string.ts +0 -38
  177. package/src/tag/tag.ts +0 -229
  178. package/src/tag/tuple.ts +0 -193
  179. package/src/tag/uint.ts +0 -51
  180. package/src/test/did.ts +0 -346
  181. package/src/utils/tag-abi-codec.ts +0 -158
@@ -0,0 +1,802 @@
1
+ import { AbiCoder } from 'ethers';
2
+ import { TagTypeParser } from '../../src/utils/tag-type-parser';
3
+ import { TagTypeBuilder } from '../../src/utils/tag-type-builder';
4
+ import { RawTagData } from '../../src/domain/types';
5
+
6
+ /**
7
+ * Unit tests for TagTypeParser
8
+ * Tests tag parsing, tuple conversion, and BigInt handling
9
+ */
10
+
11
+ describe('TagTypeParser', () => {
12
+ let abiCoder: AbiCoder;
13
+
14
+ beforeEach(() => {
15
+ abiCoder = new AbiCoder();
16
+ });
17
+
18
+ // ========================================
19
+ // Parse Tag - Basic Types
20
+ // ========================================
21
+
22
+ describe('parseTag - Basic Types', () => {
23
+ it('should parse string tag', () => {
24
+ const rawTag: RawTagData = {
25
+ from: 'global',
26
+ name: 'latestDID',
27
+ abiType: '0x03',
28
+ value: abiCoder.encode(['string'], ['did:key:z6Mk...']),
29
+ fieldNamesHash: []
30
+ };
31
+
32
+ const fieldNamesMap = new Map<string, string[]>();
33
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
34
+
35
+ expect(tag.name).toBe('latestDID');
36
+ expect(tag.abiType).toBe('string');
37
+ expect(tag.valueFormatted).toBe('did:key:z6Mk...');
38
+ });
39
+
40
+ it('should parse uint8 tag', () => {
41
+ const rawTag: RawTagData = {
42
+ from: '1.com',
43
+ name: 'age',
44
+ abiType: '0x0101',
45
+ value: abiCoder.encode(['uint8'], [30]),
46
+ fieldNamesHash: []
47
+ };
48
+
49
+ const fieldNamesMap = new Map<string, string[]>();
50
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
51
+
52
+ expect(tag.abiType).toBe('uint8');
53
+ expect(tag.valueFormatted).toBe('30');
54
+ });
55
+
56
+ it('should parse uint256 tag and convert BigInt to string', () => {
57
+ const rawTag: RawTagData = {
58
+ from: '1.com',
59
+ name: 'balance',
60
+ abiType: '0x0120',
61
+ value: abiCoder.encode(['uint256'], [1000000000000000000n]),
62
+ fieldNamesHash: []
63
+ };
64
+
65
+ const fieldNamesMap = new Map<string, string[]>();
66
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
67
+
68
+ expect(tag.abiType).toBe('uint256');
69
+ expect(tag.valueFormatted).toBe('1000000000000000000');
70
+ expect(typeof tag.valueFormatted).toBe('string');
71
+ });
72
+
73
+ it('should parse bool tag', () => {
74
+ const rawTag: RawTagData = {
75
+ from: '1.com',
76
+ name: 'verified',
77
+ abiType: '0x02',
78
+ value: abiCoder.encode(['bool'], [true]),
79
+ fieldNamesHash: []
80
+ };
81
+
82
+ const fieldNamesMap = new Map<string, string[]>();
83
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
84
+
85
+ expect(tag.abiType).toBe('bool');
86
+ expect(tag.valueFormatted).toBe(true);
87
+ });
88
+
89
+ it('should parse address tag', () => {
90
+ const rawTag: RawTagData = {
91
+ from: 'global',
92
+ name: 'owner',
93
+ abiType: '0x07',
94
+ value: abiCoder.encode(
95
+ ['address'],
96
+ ['0x742d35cc6634c0532925a3b844bc9e7595f0beb0']
97
+ ),
98
+ fieldNamesHash: []
99
+ };
100
+
101
+ const fieldNamesMap = new Map<string, string[]>();
102
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
103
+
104
+ expect(tag.abiType).toBe('address');
105
+ expect((tag.valueFormatted as string).toLowerCase()).toBe(
106
+ '0x742d35cc6634c0532925a3b844bc9e7595f0beb0'
107
+ );
108
+ });
109
+
110
+ it('should parse bytes4 tag', () => {
111
+ const rawTag: RawTagData = {
112
+ from: 'global',
113
+ name: 'dnsARecord',
114
+ abiType: '0x0804',
115
+ value: abiCoder.encode(['bytes4'], ['0xc0a80101']),
116
+ fieldNamesHash: []
117
+ };
118
+
119
+ const fieldNamesMap = new Map<string, string[]>();
120
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
121
+
122
+ expect(tag.abiType).toBe('bytes4');
123
+ expect(tag.valueFormatted).toBe('0xc0a80101');
124
+ });
125
+ });
126
+
127
+ // ========================================
128
+ // Parse Tag - Array Types
129
+ // ========================================
130
+
131
+ describe('parseTag - Array Types', () => {
132
+ it('should parse address array', () => {
133
+ const addresses = [
134
+ '0x1111111111111111111111111111111111111111',
135
+ '0x2222222222222222222222222222222222222222'
136
+ ];
137
+
138
+ const rawTag: RawTagData = {
139
+ from: '1.com',
140
+ name: 'wallets',
141
+ abiType: '0x0407',
142
+ value: abiCoder.encode(['address[]'], [addresses]),
143
+ fieldNamesHash: []
144
+ };
145
+
146
+ const fieldNamesMap = new Map<string, string[]>();
147
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
148
+
149
+ expect(tag.abiType).toBe('address[]');
150
+ const parsedAddresses = JSON.parse(tag.valueFormatted as string);
151
+ expect(Array.isArray(parsedAddresses)).toBe(true);
152
+ expect(parsedAddresses.length).toBe(2);
153
+ });
154
+
155
+ it('should parse string array', () => {
156
+ const links = ['https://x.com/alice', 'https://github.com/alice'];
157
+
158
+ const rawTag: RawTagData = {
159
+ from: '1.com',
160
+ name: 'socialLinks',
161
+ abiType: '0x0403',
162
+ value: abiCoder.encode(['string[]'], [links]),
163
+ fieldNamesHash: []
164
+ };
165
+
166
+ const fieldNamesMap = new Map<string, string[]>();
167
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
168
+
169
+ expect(tag.abiType).toBe('string[]');
170
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual(links);
171
+ });
172
+
173
+ it('should parse uint256 array and convert BigInt to string', () => {
174
+ const numbers = [100n, 200n, 300n];
175
+
176
+ const rawTag: RawTagData = {
177
+ from: '1.com',
178
+ name: 'scores',
179
+ abiType: '0x040120',
180
+ value: abiCoder.encode(['uint256[]'], [numbers]),
181
+ fieldNamesHash: []
182
+ };
183
+
184
+ const fieldNamesMap = new Map<string, string[]>();
185
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
186
+
187
+ expect(tag.abiType).toBe('uint256[]');
188
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual([
189
+ '100',
190
+ '200',
191
+ '300'
192
+ ]);
193
+ });
194
+ });
195
+
196
+ // ========================================
197
+ // Parse Tag - Simple Tuple
198
+ // ========================================
199
+
200
+ describe('parseTag - Simple Tuple', () => {
201
+ it('should parse simple tuple with field names', () => {
202
+ const value = ['Alice', 30];
203
+ const hash =
204
+ '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
205
+
206
+ const rawTag: RawTagData = {
207
+ from: '1.com',
208
+ name: 'userInfo',
209
+ abiType: '0x060002030101',
210
+ value: abiCoder.encode(['tuple(string,uint8)'], [value]),
211
+ fieldNamesHash: [hash]
212
+ };
213
+
214
+ const fieldNamesMap = new Map<string, string[]>();
215
+ fieldNamesMap.set(hash, ['name', 'age']);
216
+
217
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
218
+
219
+ expect(tag.abiType).toBe('tuple(string,uint8)');
220
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual({
221
+ name: 'Alice',
222
+ age: '30'
223
+ });
224
+ });
225
+
226
+ it('should parse tuple without field names (return array)', () => {
227
+ const value = ['Alice', 30];
228
+
229
+ const rawTag: RawTagData = {
230
+ from: '1.com',
231
+ name: 'userInfo',
232
+ abiType: '0x060002030101',
233
+ value: abiCoder.encode(['tuple(string,uint8)'], [value]),
234
+ fieldNamesHash: []
235
+ };
236
+
237
+ const fieldNamesMap = new Map<string, string[]>();
238
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
239
+
240
+ expect(tag.abiType).toBe('tuple(string,uint8)');
241
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual([
242
+ 'Alice',
243
+ '30'
244
+ ]);
245
+ });
246
+
247
+ it('should parse tuple with complex types', () => {
248
+ const value = [
249
+ '0x742d35cc6634c0532925a3b844bc9e7595f0beb0',
250
+ 1000n,
251
+ true
252
+ ];
253
+ const hash =
254
+ '0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890';
255
+
256
+ const rawTag: RawTagData = {
257
+ from: '1.com',
258
+ name: 'profile',
259
+ abiType: '0x060003070120020',
260
+ value: abiCoder.encode(
261
+ ['tuple(address,uint256,bool)'],
262
+ [value]
263
+ ),
264
+ fieldNamesHash: [hash]
265
+ };
266
+
267
+ const fieldNamesMap = new Map<string, string[]>();
268
+ fieldNamesMap.set(hash, ['owner', 'balance', 'active']);
269
+
270
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
271
+
272
+ expect(tag.abiType).toBe('tuple(address,uint256,bool)');
273
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual({
274
+ owner: expect.any(String),
275
+ balance: '1000',
276
+ active: true
277
+ });
278
+ });
279
+ });
280
+
281
+ // ========================================
282
+ // Parse Tag - Nested Tuple
283
+ // ========================================
284
+
285
+ describe('parseTag - Nested Tuple', () => {
286
+ it('should parse nested tuple with field names', () => {
287
+ const value = ['Bob', [25, true]];
288
+ const hash1 =
289
+ '0x1111111111111111111111111111111111111111111111111111111111111111';
290
+ const hash2 =
291
+ '0x2222222222222222222222222222222222222222222222222222222222222222';
292
+
293
+ const rawTag: RawTagData = {
294
+ from: '1.com',
295
+ name: 'profile',
296
+ abiType: '0x06000203060002010102',
297
+ value: abiCoder.encode(
298
+ ['tuple(string,tuple(uint8,bool))'],
299
+ [value]
300
+ ),
301
+ fieldNamesHash: [hash1, hash2]
302
+ };
303
+
304
+ const fieldNamesMap = new Map<string, string[]>();
305
+ fieldNamesMap.set(hash1, ['name', 'details']);
306
+ fieldNamesMap.set(hash2, ['age', 'verified']);
307
+
308
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
309
+
310
+ expect(tag.abiType).toBe('tuple(string,tuple(uint8,bool))');
311
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual({
312
+ name: 'Bob',
313
+ details: {
314
+ age: '25',
315
+ verified: true
316
+ }
317
+ });
318
+ });
319
+
320
+ it('should parse deeply nested tuple', () => {
321
+ const value = ['Alice', [30, ['alice@example.com', '+1-555-0123']]];
322
+ const hash1 =
323
+ '0xaaaa111111111111111111111111111111111111111111111111111111111111';
324
+ const hash2 =
325
+ '0xbbbb222222222222222222222222222222222222222222222222222222222222';
326
+ const hash3 =
327
+ '0xcccc333333333333333333333333333333333333333333333333333333333333';
328
+
329
+ const rawTag: RawTagData = {
330
+ from: '1.com',
331
+ name: 'profile',
332
+ abiType: '0x06000203060002010106000203030',
333
+ value: abiCoder.encode(
334
+ ['tuple(string,tuple(uint8,tuple(string,string)))'],
335
+ [value]
336
+ ),
337
+ fieldNamesHash: [hash1, hash2, hash3]
338
+ };
339
+
340
+ const fieldNamesMap = new Map<string, string[]>();
341
+ fieldNamesMap.set(hash1, ['name', 'details']);
342
+ fieldNamesMap.set(hash2, ['age', 'contact']);
343
+ fieldNamesMap.set(hash3, ['email', 'phone']);
344
+
345
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
346
+
347
+ expect(tag.abiType).toBe(
348
+ 'tuple(string,tuple(uint8,tuple(string,string)))'
349
+ );
350
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual({
351
+ name: 'Alice',
352
+ details: {
353
+ age: '30',
354
+ contact: {
355
+ email: 'alice@example.com',
356
+ phone: '+1-555-0123'
357
+ }
358
+ }
359
+ });
360
+ });
361
+ });
362
+
363
+ // ========================================
364
+ // Parse Tag - Tuple Array
365
+ // ========================================
366
+
367
+ describe('parseTag - Tuple Array', () => {
368
+ it('should parse simple tuple array', () => {
369
+ const value = [
370
+ [0, '0x742d35cc6634c0532925a3b844bc9e7595f0beb0'],
371
+ [1, '0x1111111111111111111111111111111111111111']
372
+ ];
373
+ const hash =
374
+ '0xa7011a7789e2d5350dd5d7f1cd1b05f9c6948c18fcad68f64983c8488b9b5625';
375
+
376
+ const rawTag: RawTagData = {
377
+ from: '',
378
+ name: 'authAddresses',
379
+ abiType: '0x04060002010107',
380
+ value: abiCoder.encode(['tuple(uint8,address)[]'], [value]),
381
+ fieldNamesHash: [hash]
382
+ };
383
+
384
+ const fieldNamesMap = new Map<string, string[]>();
385
+ fieldNamesMap.set(hash, ['algorithm', 'addr']);
386
+
387
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
388
+
389
+ expect(tag.abiType).toBe('tuple(uint8,address)[]');
390
+ const parsedAuthAddr = JSON.parse(tag.valueFormatted as string);
391
+ expect(Array.isArray(parsedAuthAddr)).toBe(true);
392
+ expect(parsedAuthAddr).toEqual([
393
+ { algorithm: '0', addr: expect.any(String) },
394
+ { algorithm: '1', addr: expect.any(String) }
395
+ ]);
396
+ });
397
+
398
+ it('should parse nested tuple array', () => {
399
+ const value = [
400
+ [1, ['Alice', true]],
401
+ [2, ['Bob', false]]
402
+ ];
403
+ const hash1 =
404
+ '0x1111111111111111111111111111111111111111111111111111111111111111';
405
+ const hash2 =
406
+ '0x2222222222222222222222222222222222222222222222222222222222222222';
407
+
408
+ const rawTag: RawTagData = {
409
+ from: '1.com',
410
+ name: 'items',
411
+ abiType: '0x04060002012006000203020',
412
+ value: abiCoder.encode(
413
+ ['tuple(uint256,tuple(string,bool))[]'],
414
+ [value]
415
+ ),
416
+ fieldNamesHash: [hash1, hash2]
417
+ };
418
+
419
+ const fieldNamesMap = new Map<string, string[]>();
420
+ fieldNamesMap.set(hash1, ['id', 'data']);
421
+ fieldNamesMap.set(hash2, ['name', 'active']);
422
+
423
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
424
+
425
+ expect(tag.abiType).toBe('tuple(uint256,tuple(string,bool))[]');
426
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual([
427
+ { id: '1', data: { name: 'Alice', active: true } },
428
+ { id: '2', data: { name: 'Bob', active: false } }
429
+ ]);
430
+ });
431
+
432
+ it('should parse empty tuple array', () => {
433
+ const value: any[] = [];
434
+ const hash =
435
+ '0xa7011a7789e2d5350dd5d7f1cd1b05f9c6948c18fcad68f64983c8488b9b5625';
436
+
437
+ const rawTag: RawTagData = {
438
+ from: '',
439
+ name: 'authAddresses',
440
+ abiType: '0x04060002010107',
441
+ value: abiCoder.encode(['tuple(uint8,address)[]'], [value]),
442
+ fieldNamesHash: [hash]
443
+ };
444
+
445
+ const fieldNamesMap = new Map<string, string[]>();
446
+ fieldNamesMap.set(hash, ['algorithm', 'addr']);
447
+
448
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
449
+
450
+ expect(tag.abiType).toBe('tuple(uint8,address)[]');
451
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual([]);
452
+ });
453
+ });
454
+
455
+ // ========================================
456
+ // Parse Tag - Complex Nested Tuples
457
+ // ========================================
458
+
459
+ describe('parseTag - Complex Nested Tuples', () => {
460
+ it('should parse tuple with multiple nested tuples', () => {
461
+ const value = [
462
+ 'Alice Johnson',
463
+ [30, true],
464
+ ['alice@example.com', '+1-555-0123']
465
+ ];
466
+ const hash1 =
467
+ '0xbd67eb3ac6cf9025dadacf21cbc2fc2db499fc6154f0f8ff2ff87e114d60ad27';
468
+ const hash2 =
469
+ '0x2f85d0ec24ccc16f8739d2e76938c4e5104a4438ce0d6d2f9a793527d77aaf20';
470
+ const hash3 =
471
+ '0xc414cfe30fe6ad154c340fa4048fd9d2e36e551d4860498cb63f84188b08ffa4';
472
+
473
+ const rawTag: RawTagData = {
474
+ from: '1.com',
475
+ name: 'profile',
476
+ abiType: '0x06000303060002010102060002030303',
477
+ value: abiCoder.encode(
478
+ ['tuple(string,tuple(uint8,bool),tuple(string,string))'],
479
+ [value]
480
+ ),
481
+ fieldNamesHash: [hash1, hash2, hash3]
482
+ };
483
+
484
+ const fieldNamesMap = new Map<string, string[]>();
485
+ fieldNamesMap.set(hash1, ['name', 'details', 'contact']);
486
+ fieldNamesMap.set(hash2, ['age', 'verified']);
487
+ fieldNamesMap.set(hash3, ['email', 'phone']);
488
+
489
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
490
+
491
+ expect(tag.abiType).toBe(
492
+ 'tuple(string,tuple(uint8,bool),tuple(string,string))'
493
+ );
494
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual({
495
+ name: 'Alice Johnson',
496
+ details: {
497
+ age: '30',
498
+ verified: true
499
+ },
500
+ contact: {
501
+ email: 'alice@example.com',
502
+ phone: '+1-555-0123'
503
+ }
504
+ });
505
+ });
506
+
507
+ it('should parse tuple with array field', () => {
508
+ const value = [
509
+ 'Alice',
510
+ 31,
511
+ [
512
+ '0x1111111111111111111111111111111111111111',
513
+ '0x2222222222222222222222222222222222222222'
514
+ ],
515
+ true
516
+ ];
517
+ const hash =
518
+ '0xf1e429c1ee11d289bde872dd17c5937a9da2ad9212b711c15ff5afe8828ebcba';
519
+
520
+ const rawTag: RawTagData = {
521
+ from: '1.com',
522
+ name: 'userInfo',
523
+ abiType: '0x0600040301010407020',
524
+ value: abiCoder.encode(
525
+ ['tuple(string,uint8,address[],bool)'],
526
+ [value]
527
+ ),
528
+ fieldNamesHash: [hash]
529
+ };
530
+
531
+ const fieldNamesMap = new Map<string, string[]>();
532
+ fieldNamesMap.set(hash, ['name', 'age', 'wallets', 'verified']);
533
+
534
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
535
+
536
+ expect(tag.abiType).toBe('tuple(string,uint8,address[],bool)');
537
+ const parsedTuple = JSON.parse(tag.valueFormatted as string);
538
+ expect(parsedTuple).toEqual({
539
+ name: 'Alice',
540
+ age: '31',
541
+ wallets: expect.any(Array),
542
+ verified: true
543
+ });
544
+ expect(parsedTuple.wallets.length).toBe(2);
545
+ });
546
+ });
547
+
548
+ // ========================================
549
+ // convertTupleToObject - Direct Tests
550
+ // ========================================
551
+
552
+ describe('convertTupleToObject', () => {
553
+ it('should convert simple tuple array to object', () => {
554
+ const value = ['Alice', 30];
555
+ const allFieldNames = [['name', 'age']];
556
+ const context = { fieldNameIndex: 0 };
557
+
558
+ const result = TagTypeParser.convertTupleToObject(
559
+ value,
560
+ 'tuple(string,uint8)',
561
+ allFieldNames,
562
+ context
563
+ );
564
+
565
+ expect(result).toEqual({ name: 'Alice', age: 30 });
566
+ expect(context.fieldNameIndex).toBe(1);
567
+ });
568
+
569
+ it('should convert nested tuple to object', () => {
570
+ const value = ['Bob', [25, true]];
571
+ const allFieldNames = [
572
+ ['name', 'profile'],
573
+ ['age', 'verified']
574
+ ];
575
+ const context = { fieldNameIndex: 0 };
576
+
577
+ const result = TagTypeParser.convertTupleToObject(
578
+ value,
579
+ 'tuple(string,tuple(uint8,bool))',
580
+ allFieldNames,
581
+ context
582
+ );
583
+
584
+ expect(result).toEqual({
585
+ name: 'Bob',
586
+ profile: { age: 25, verified: true }
587
+ });
588
+ expect(context.fieldNameIndex).toBe(2);
589
+ });
590
+
591
+ it('should handle field name/value length mismatch', () => {
592
+ const value = ['Alice', 30, 'extra'];
593
+ const allFieldNames = [['name', 'age']]; // Only 2 field names
594
+ const context = { fieldNameIndex: 0 };
595
+
596
+ const result = TagTypeParser.convertTupleToObject(
597
+ value,
598
+ 'tuple(string,uint8)',
599
+ allFieldNames,
600
+ context
601
+ );
602
+
603
+ // Should return original value when mismatch
604
+ expect(result).toEqual(value);
605
+ });
606
+
607
+ it('should handle missing field names', () => {
608
+ const value = ['Alice', 30];
609
+ const allFieldNames: string[][] = []; // No field names
610
+ const context = { fieldNameIndex: 0 };
611
+
612
+ const result = TagTypeParser.convertTupleToObject(
613
+ value,
614
+ 'tuple(string,uint8)',
615
+ allFieldNames,
616
+ context
617
+ );
618
+
619
+ // Should return original value when no field names
620
+ expect(result).toEqual(value);
621
+ });
622
+
623
+ it('should return non-array values as-is', () => {
624
+ const value = 'not-an-array';
625
+ const allFieldNames = [['name', 'age']];
626
+ const context = { fieldNameIndex: 0 };
627
+
628
+ const result = TagTypeParser.convertTupleToObject(
629
+ value,
630
+ 'tuple(string,uint8)',
631
+ allFieldNames,
632
+ context
633
+ );
634
+
635
+ expect(result).toBe(value);
636
+ });
637
+ });
638
+
639
+ // ========================================
640
+ // convertBigIntToString - Direct Tests
641
+ // ========================================
642
+
643
+ describe('convertBigIntToString', () => {
644
+ it('should convert BigInt to string', () => {
645
+ const result = TagTypeParser.convertBigIntToString(1000n);
646
+ expect(result).toBe('1000');
647
+ expect(typeof result).toBe('string');
648
+ });
649
+
650
+ it('should convert BigInt in array', () => {
651
+ const result = TagTypeParser.convertBigIntToString([
652
+ 100n,
653
+ 200n,
654
+ 300n
655
+ ]);
656
+ expect(result).toEqual(['100', '200', '300']);
657
+ });
658
+
659
+ it('should convert BigInt in object', () => {
660
+ const result = TagTypeParser.convertBigIntToString({
661
+ balance: 1000n,
662
+ amount: 2000n
663
+ });
664
+ expect(result).toEqual({
665
+ balance: '1000',
666
+ amount: '2000'
667
+ });
668
+ });
669
+
670
+ it('should handle nested structures with BigInt', () => {
671
+ const result = TagTypeParser.convertBigIntToString({
672
+ user: {
673
+ balances: [100n, 200n],
674
+ total: 300n
675
+ },
676
+ active: true
677
+ });
678
+ expect(result).toEqual({
679
+ user: {
680
+ balances: ['100', '200'],
681
+ total: '300'
682
+ },
683
+ active: true
684
+ });
685
+ });
686
+
687
+ it('should handle primitives and non-BigInt values', () => {
688
+ expect(TagTypeParser.convertBigIntToString('hello')).toBe('hello');
689
+ expect(TagTypeParser.convertBigIntToString(123)).toBe(123);
690
+ expect(TagTypeParser.convertBigIntToString(true)).toBe(true);
691
+ expect(TagTypeParser.convertBigIntToString(null)).toBe(null);
692
+ });
693
+ });
694
+
695
+ // ========================================
696
+ // Edge Cases
697
+ // ========================================
698
+
699
+ describe('Edge Cases', () => {
700
+ it('should handle tuple with all primitive types', () => {
701
+ const value = [
702
+ 'test',
703
+ 123,
704
+ true,
705
+ '0x742d35cc6634c0532925a3b844bc9e7595f0beb0',
706
+ '0xabcdef',
707
+ 100n
708
+ ];
709
+ const hash =
710
+ '0xedge111111111111111111111111111111111111111111111111111111111111';
711
+
712
+ const rawTag: RawTagData = {
713
+ from: '1.com',
714
+ name: 'mixed',
715
+ abiType: '0x0600060301010207090120',
716
+ value: abiCoder.encode(
717
+ ['tuple(string,uint8,bool,address,bytes,uint256)'],
718
+ [value]
719
+ ),
720
+ fieldNamesHash: [hash]
721
+ };
722
+
723
+ const fieldNamesMap = new Map<string, string[]>();
724
+ fieldNamesMap.set(hash, [
725
+ 'str',
726
+ 'num',
727
+ 'flag',
728
+ 'addr',
729
+ 'data',
730
+ 'balance'
731
+ ]);
732
+
733
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
734
+
735
+ expect(tag.abiType).toBe(
736
+ 'tuple(string,uint8,bool,address,bytes,uint256)'
737
+ );
738
+ const parsedMixed = JSON.parse(tag.valueFormatted as string);
739
+ expect(parsedMixed.str).toBe('test');
740
+ expect(parsedMixed.num).toBe('123');
741
+ expect(parsedMixed.flag).toBe(true);
742
+ expect(parsedMixed.balance).toBe('100');
743
+ });
744
+
745
+ it('should warn when field names not found for tuple', () => {
746
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
747
+
748
+ const value = ['Alice', 30];
749
+ const hash =
750
+ '0xnonexistent1111111111111111111111111111111111111111111111111111';
751
+
752
+ const rawTag: RawTagData = {
753
+ from: '1.com',
754
+ name: 'userInfo',
755
+ abiType: '0x060002030101',
756
+ value: abiCoder.encode(['tuple(string,uint8)'], [value]),
757
+ fieldNamesHash: [hash]
758
+ };
759
+
760
+ const fieldNamesMap = new Map<string, string[]>();
761
+ // Not adding the hash to fieldNamesMap
762
+
763
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
764
+
765
+ // Should have been called with the warning message
766
+ expect(warnSpy).toHaveBeenCalledTimes(1);
767
+ const callArgs = warnSpy.mock.calls[0];
768
+ expect(callArgs[0]).toContain('Field names not found');
769
+ expect(callArgs[0]).toContain('userInfo');
770
+
771
+ expect(JSON.parse(tag.valueFormatted as string)).toEqual([
772
+ 'Alice',
773
+ '30'
774
+ ]); // Falls back to array
775
+
776
+ warnSpy.mockRestore();
777
+ });
778
+
779
+ it('should not warn for zero hash', () => {
780
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
781
+
782
+ const value = ['Alice', 30];
783
+
784
+ const rawTag: RawTagData = {
785
+ from: '1.com',
786
+ name: 'userInfo',
787
+ abiType: '0x060002030101',
788
+ value: abiCoder.encode(['tuple(string,uint8)'], [value]),
789
+ fieldNamesHash: [
790
+ '0x0000000000000000000000000000000000000000000000000000000000000000'
791
+ ]
792
+ };
793
+
794
+ const fieldNamesMap = new Map<string, string[]>();
795
+ const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
796
+
797
+ expect(warnSpy).toHaveBeenCalledTimes(0);
798
+
799
+ warnSpy.mockRestore();
800
+ });
801
+ });
802
+ });