@beclab/olaresid 0.1.12 → 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 (182) hide show
  1. package/CLI-TREE.md +107 -0
  2. package/CLI.md +122 -1329
  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 +1 -1
  12. package/dist/business/index.d.ts.map +1 -1
  13. package/dist/business/index.js +11 -24
  14. package/dist/business/index.js.map +1 -1
  15. package/dist/business/tag-context.d.ts +1 -0
  16. package/dist/business/tag-context.d.ts.map +1 -1
  17. package/dist/business/tag-context.js +13 -7
  18. package/dist/business/tag-context.js.map +1 -1
  19. package/dist/cli.js +238 -107
  20. package/dist/cli.js.map +1 -1
  21. package/dist/config/index.d.ts +16 -4
  22. package/dist/config/index.d.ts.map +1 -1
  23. package/dist/config/index.js +28 -14
  24. package/dist/config/index.js.map +1 -1
  25. package/dist/domain/core.d.ts +65 -0
  26. package/dist/domain/core.d.ts.map +1 -0
  27. package/dist/domain/core.js +317 -0
  28. package/dist/domain/core.js.map +1 -0
  29. package/dist/domain/index.d.ts +104 -57
  30. package/dist/domain/index.d.ts.map +1 -1
  31. package/dist/domain/index.js +188 -428
  32. package/dist/domain/index.js.map +1 -1
  33. package/dist/domain/types.d.ts +56 -0
  34. package/dist/domain/types.d.ts.map +1 -0
  35. package/dist/domain/types.js +3 -0
  36. package/dist/domain/types.js.map +1 -0
  37. package/dist/index.d.ts +81 -24
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +153 -143
  40. package/dist/index.js.map +1 -1
  41. package/dist/utils/crypto-utils.d.ts +124 -0
  42. package/dist/utils/crypto-utils.d.ts.map +1 -1
  43. package/dist/utils/crypto-utils.js +156 -8
  44. package/dist/utils/crypto-utils.js.map +1 -1
  45. package/dist/utils/error-parser.d.ts.map +1 -1
  46. package/dist/utils/error-parser.js +2 -1
  47. package/dist/utils/error-parser.js.map +1 -1
  48. package/dist/utils/event-parser.d.ts +161 -0
  49. package/dist/utils/event-parser.d.ts.map +1 -0
  50. package/dist/utils/event-parser.js +140 -0
  51. package/dist/utils/event-parser.js.map +1 -0
  52. package/dist/utils/tag-type-builder.d.ts +43 -0
  53. package/dist/utils/tag-type-builder.d.ts.map +1 -1
  54. package/dist/utils/tag-type-builder.js +122 -0
  55. package/dist/utils/tag-type-builder.js.map +1 -1
  56. package/dist/utils/tag-type-parser.d.ts +70 -0
  57. package/dist/utils/tag-type-parser.d.ts.map +1 -0
  58. package/dist/utils/tag-type-parser.js +190 -0
  59. package/dist/utils/tag-type-parser.js.map +1 -0
  60. package/examples/create-with-rpc-demo.ts +142 -0
  61. package/examples/fetch-all-flat-demo.ts +159 -0
  62. package/examples/fetch-by-indices-demo.ts +235 -0
  63. package/examples/fetch-domain-demo.ts +137 -0
  64. package/examples/fetch-domains-demo.ts +221 -0
  65. package/examples/frontend-demo/index.html +2 -2
  66. package/examples/frontend-demo/package-lock.json +4 -1
  67. package/examples/index.ts +3 -5
  68. package/jest.config.js +25 -0
  69. package/package.json +6 -2
  70. package/src/abi/TerminusDIDQueryABI.ts +516 -0
  71. package/src/business/index.ts +10 -33
  72. package/src/business/tag-context.ts +35 -7
  73. package/src/cli.ts +344 -121
  74. package/src/config/index.ts +34 -19
  75. package/src/domain/core.ts +382 -0
  76. package/src/domain/index.ts +271 -641
  77. package/src/domain/types.ts +59 -0
  78. package/src/index.ts +222 -207
  79. package/src/utils/crypto-utils.ts +239 -2
  80. package/src/utils/error-parser.ts +2 -1
  81. package/src/utils/event-parser.ts +353 -0
  82. package/src/utils/tag-type-builder.ts +138 -0
  83. package/src/utils/tag-type-parser.ts +246 -0
  84. package/tests/unit/crypto-utils.test.ts +338 -0
  85. package/tests/unit/ed25519-jwk.test.ts +201 -0
  86. package/tests/unit/event-parser.test.ts +690 -0
  87. package/tests/unit/generate-mnemonic.test.ts +268 -0
  88. package/tests/unit/olares-id-format.test.ts +321 -0
  89. package/tests/unit/tag-type-parser.test.ts +802 -0
  90. package/tests/unit/tag-types.test.ts +821 -0
  91. package/tsconfig.json +3 -2
  92. package/dist/abi/ABITypeABI.d.ts +0 -88
  93. package/dist/abi/ABITypeABI.d.ts.map +0 -1
  94. package/dist/abi/ABITypeABI.js +0 -382
  95. package/dist/abi/ABITypeABI.js.map +0 -1
  96. package/dist/abi/RegistryABI.d.ts +0 -77
  97. package/dist/abi/RegistryABI.d.ts.map +0 -1
  98. package/dist/abi/RegistryABI.js +0 -462
  99. package/dist/abi/RegistryABI.js.map +0 -1
  100. package/dist/tag/address.d.ts +0 -11
  101. package/dist/tag/address.d.ts.map +0 -1
  102. package/dist/tag/address.js +0 -44
  103. package/dist/tag/address.js.map +0 -1
  104. package/dist/tag/array.d.ts +0 -14
  105. package/dist/tag/array.d.ts.map +0 -1
  106. package/dist/tag/array.js +0 -72
  107. package/dist/tag/array.js.map +0 -1
  108. package/dist/tag/bool.d.ts +0 -11
  109. package/dist/tag/bool.d.ts.map +0 -1
  110. package/dist/tag/bool.js +0 -43
  111. package/dist/tag/bool.js.map +0 -1
  112. package/dist/tag/bytes.d.ts +0 -11
  113. package/dist/tag/bytes.d.ts.map +0 -1
  114. package/dist/tag/bytes.js +0 -37
  115. package/dist/tag/bytes.js.map +0 -1
  116. package/dist/tag/flarray.d.ts +0 -15
  117. package/dist/tag/flarray.d.ts.map +0 -1
  118. package/dist/tag/flarray.js +0 -81
  119. package/dist/tag/flarray.js.map +0 -1
  120. package/dist/tag/flbytes.d.ts +0 -11
  121. package/dist/tag/flbytes.d.ts.map +0 -1
  122. package/dist/tag/flbytes.js +0 -47
  123. package/dist/tag/flbytes.js.map +0 -1
  124. package/dist/tag/index.d.ts +0 -32
  125. package/dist/tag/index.d.ts.map +0 -1
  126. package/dist/tag/index.js +0 -121
  127. package/dist/tag/index.js.map +0 -1
  128. package/dist/tag/int.d.ts +0 -12
  129. package/dist/tag/int.d.ts.map +0 -1
  130. package/dist/tag/int.js +0 -49
  131. package/dist/tag/int.js.map +0 -1
  132. package/dist/tag/string.d.ts +0 -11
  133. package/dist/tag/string.d.ts.map +0 -1
  134. package/dist/tag/string.js +0 -37
  135. package/dist/tag/string.js.map +0 -1
  136. package/dist/tag/tag.d.ts +0 -67
  137. package/dist/tag/tag.d.ts.map +0 -1
  138. package/dist/tag/tag.js +0 -157
  139. package/dist/tag/tag.js.map +0 -1
  140. package/dist/tag/tuple.d.ts +0 -17
  141. package/dist/tag/tuple.d.ts.map +0 -1
  142. package/dist/tag/tuple.js +0 -162
  143. package/dist/tag/tuple.js.map +0 -1
  144. package/dist/tag/uint.d.ts +0 -12
  145. package/dist/tag/uint.d.ts.map +0 -1
  146. package/dist/tag/uint.js +0 -49
  147. package/dist/tag/uint.js.map +0 -1
  148. package/dist/test/did.d.ts +0 -2
  149. package/dist/test/did.d.ts.map +0 -1
  150. package/dist/test/did.js +0 -177
  151. package/dist/test/did.js.map +0 -1
  152. package/dist/utils/tag-abi-codec.d.ts +0 -69
  153. package/dist/utils/tag-abi-codec.d.ts.map +0 -1
  154. package/dist/utils/tag-abi-codec.js +0 -144
  155. package/dist/utils/tag-abi-codec.js.map +0 -1
  156. package/examples/crypto-utilities.ts +0 -140
  157. package/examples/ed25519-jwk.ts +0 -73
  158. package/examples/generate-mnemonic.ts +0 -149
  159. package/examples/legacy.ts +0 -33
  160. package/examples/olares-id-format.ts +0 -197
  161. package/examples/tag-builder.ts +0 -235
  162. package/examples/tag-nested-tuple.ts +0 -190
  163. package/examples/tag-simple.ts +0 -149
  164. package/examples/tag-tagger.ts +0 -217
  165. package/examples/test-nested-tuple-conversion.ts +0 -143
  166. package/examples/test-type-bytes-parser.ts +0 -70
  167. package/src/abi/ABITypeABI.ts +0 -379
  168. package/src/abi/RegistryABI.ts +0 -459
  169. package/src/tag/address.ts +0 -48
  170. package/src/tag/array.ts +0 -80
  171. package/src/tag/bool.ts +0 -43
  172. package/src/tag/bytes.ts +0 -38
  173. package/src/tag/flarray.ts +0 -99
  174. package/src/tag/flbytes.ts +0 -48
  175. package/src/tag/index.ts +0 -170
  176. package/src/tag/int.ts +0 -51
  177. package/src/tag/string.ts +0 -38
  178. package/src/tag/tag.ts +0 -229
  179. package/src/tag/tuple.ts +0 -193
  180. package/src/tag/uint.ts +0 -51
  181. package/src/test/did.ts +0 -346
  182. package/src/utils/tag-abi-codec.ts +0 -158
@@ -0,0 +1,821 @@
1
+ import { AbiCoder } from 'ethers';
2
+ import { TagTypeBuilder } from '../../src/utils/tag-type-builder';
3
+
4
+ /**
5
+ * Comprehensive tests for Tag type encoding/decoding
6
+ * Covers all tag types: basic, arrays, tuples, and nested structures
7
+ *
8
+ * Run: npm run test:unit
9
+ */
10
+
11
+ describe('Tag Type Encoding/Decoding', () => {
12
+ let abiCoder: AbiCoder;
13
+
14
+ beforeAll(() => {
15
+ abiCoder = new AbiCoder();
16
+ });
17
+
18
+ // ========================================
19
+ // Basic Types
20
+ // ========================================
21
+
22
+ describe('Basic Types', () => {
23
+ it('should handle string type', () => {
24
+ const builder = TagTypeBuilder.string();
25
+ const typeBytes = '0x' + builder.getTypeBytes();
26
+ const abiType = builder.getAbiTypeString();
27
+
28
+ expect(abiType).toBe('string');
29
+
30
+ // Parse back
31
+ const parsedType =
32
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
33
+ expect(parsedType).toBe('string');
34
+
35
+ // Encode/Decode
36
+ const value = 'Hello World';
37
+ const encoded = abiCoder.encode([abiType], [value]);
38
+ const decoded = abiCoder.decode([abiType], encoded);
39
+ expect(decoded[0]).toBe(value);
40
+ });
41
+
42
+ it('should handle uint256 type', () => {
43
+ const builder = TagTypeBuilder.uint256();
44
+ const typeBytes = '0x' + builder.getTypeBytes();
45
+ const abiType = builder.getAbiTypeString();
46
+
47
+ expect(abiType).toBe('uint256');
48
+ expect(typeBytes).toBe('0x0120');
49
+
50
+ const parsedType =
51
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
52
+ expect(parsedType).toBe('uint256');
53
+
54
+ const value = 12345n;
55
+ const encoded = abiCoder.encode([abiType], [value]);
56
+ const decoded = abiCoder.decode([abiType], encoded);
57
+ expect(decoded[0]).toBe(value);
58
+ });
59
+
60
+ it('should handle uint8 type', () => {
61
+ const builder = TagTypeBuilder.uint8();
62
+ const abiType = builder.getAbiTypeString();
63
+
64
+ expect(abiType).toBe('uint8');
65
+
66
+ const value = 255n;
67
+ const encoded = abiCoder.encode([abiType], [value]);
68
+ const decoded = abiCoder.decode([abiType], encoded);
69
+ expect(decoded[0]).toBe(value);
70
+ });
71
+
72
+ it('should handle int256 type', () => {
73
+ const builder = TagTypeBuilder.int256();
74
+ const abiType = builder.getAbiTypeString();
75
+
76
+ expect(abiType).toBe('int256');
77
+
78
+ const value = -12345n;
79
+ const encoded = abiCoder.encode([abiType], [value]);
80
+ const decoded = abiCoder.decode([abiType], encoded);
81
+ expect(decoded[0]).toBe(value);
82
+ });
83
+
84
+ it('should handle bool type', () => {
85
+ const builder = TagTypeBuilder.bool();
86
+ const abiType = builder.getAbiTypeString();
87
+
88
+ expect(abiType).toBe('bool');
89
+
90
+ const value = true;
91
+ const encoded = abiCoder.encode([abiType], [value]);
92
+ const decoded = abiCoder.decode([abiType], encoded);
93
+ expect(decoded[0]).toBe(value);
94
+ });
95
+
96
+ it('should handle address type', () => {
97
+ const builder = TagTypeBuilder.address();
98
+ const typeBytes = '0x' + builder.getTypeBytes();
99
+ const abiType = builder.getAbiTypeString();
100
+
101
+ expect(abiType).toBe('address');
102
+ expect(typeBytes).toBe('0x07');
103
+
104
+ const parsedType =
105
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
106
+ expect(parsedType).toBe('address');
107
+
108
+ const value = '0x742d35cc6634c0532925a3b844bc9e7595f0beb0';
109
+ const encoded = abiCoder.encode([abiType], [value]);
110
+ const decoded = abiCoder.decode([abiType], encoded);
111
+ expect(decoded[0].toLowerCase()).toBe(value.toLowerCase());
112
+ });
113
+
114
+ it('should handle bytes type', () => {
115
+ const builder = TagTypeBuilder.bytes();
116
+ const abiType = builder.getAbiTypeString();
117
+
118
+ expect(abiType).toBe('bytes');
119
+
120
+ const value = '0x1234567890abcdef';
121
+ const encoded = abiCoder.encode([abiType], [value]);
122
+ const decoded = abiCoder.decode([abiType], encoded);
123
+ expect(decoded[0]).toBe(value);
124
+ });
125
+
126
+ it('should handle bytes32 type', () => {
127
+ const builder = TagTypeBuilder.bytes32();
128
+ const typeBytes = '0x' + builder.getTypeBytes();
129
+ const abiType = builder.getAbiTypeString();
130
+
131
+ expect(abiType).toBe('bytes32');
132
+ expect(typeBytes).toBe('0x0820');
133
+
134
+ const parsedType =
135
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
136
+ expect(parsedType).toBe('bytes32');
137
+
138
+ const value =
139
+ '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
140
+ const encoded = abiCoder.encode([abiType], [value]);
141
+ const decoded = abiCoder.decode([abiType], encoded);
142
+ expect(decoded[0]).toBe(value);
143
+ });
144
+
145
+ it('should handle bytes4 type', () => {
146
+ const builder = TagTypeBuilder.fixedBytes(4);
147
+ const abiType = builder.getAbiTypeString();
148
+
149
+ expect(abiType).toBe('bytes4');
150
+
151
+ const value = '0x12345678';
152
+ const encoded = abiCoder.encode([abiType], [value]);
153
+ const decoded = abiCoder.decode([abiType], encoded);
154
+ expect(decoded[0]).toBe(value);
155
+ });
156
+ });
157
+
158
+ // ========================================
159
+ // Array Types
160
+ // ========================================
161
+
162
+ describe('Array Types', () => {
163
+ it('should handle string[] type', () => {
164
+ const builder = TagTypeBuilder.stringArray();
165
+ const typeBytes = '0x' + builder.getTypeBytes();
166
+ const abiType = builder.getAbiTypeString();
167
+
168
+ expect(abiType).toBe('string[]');
169
+
170
+ const parsedType =
171
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
172
+ expect(parsedType).toBe('string[]');
173
+
174
+ const value = ['hello', 'world', 'test'];
175
+ const encoded = abiCoder.encode([abiType], [value]);
176
+ const decoded = abiCoder.decode([abiType], encoded);
177
+ expect(decoded[0]).toEqual(value);
178
+ });
179
+
180
+ it('should handle uint256[] type', () => {
181
+ const builder = TagTypeBuilder.uint256Array();
182
+ const abiType = builder.getAbiTypeString();
183
+
184
+ expect(abiType).toBe('uint256[]');
185
+
186
+ const value = [1n, 2n, 3n, 100n];
187
+ const encoded = abiCoder.encode([abiType], [value]);
188
+ const decoded = abiCoder.decode([abiType], encoded);
189
+ expect(decoded[0]).toEqual(value);
190
+ });
191
+
192
+ it('should handle address[] type', () => {
193
+ const builder = TagTypeBuilder.addressArray();
194
+ const abiType = builder.getAbiTypeString();
195
+
196
+ expect(abiType).toBe('address[]');
197
+
198
+ const value = [
199
+ '0x742d35cc6634c0532925a3b844bc9e7595f0beb0',
200
+ '0x1234567890123456789012345678901234567890'
201
+ ];
202
+ const encoded = abiCoder.encode([abiType], [value]);
203
+ const decoded = abiCoder.decode([abiType], encoded);
204
+ expect(decoded[0].map((a: string) => a.toLowerCase())).toEqual(
205
+ value.map((a) => a.toLowerCase())
206
+ );
207
+ });
208
+
209
+ it('should handle bool[] type', () => {
210
+ const builder = TagTypeBuilder.array(TagTypeBuilder.bool());
211
+ const abiType = builder.getAbiTypeString();
212
+
213
+ expect(abiType).toBe('bool[]');
214
+
215
+ const value = [true, false, true, true];
216
+ const encoded = abiCoder.encode([abiType], [value]);
217
+ const decoded = abiCoder.decode([abiType], encoded);
218
+ expect(decoded[0]).toEqual(value);
219
+ });
220
+
221
+ it('should handle fixed-length array string[3]', () => {
222
+ const builder = TagTypeBuilder.fixedArray(
223
+ TagTypeBuilder.string(),
224
+ 3
225
+ );
226
+ const typeBytes = '0x' + builder.getTypeBytes();
227
+ const abiType = builder.getAbiTypeString();
228
+
229
+ expect(abiType).toBe('string[3]');
230
+
231
+ const parsedType =
232
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
233
+ expect(parsedType).toBe('string[3]');
234
+
235
+ const value = ['a', 'b', 'c'];
236
+ const encoded = abiCoder.encode([abiType], [value]);
237
+ const decoded = abiCoder.decode([abiType], encoded);
238
+ expect(decoded[0]).toEqual(value);
239
+ });
240
+ });
241
+
242
+ // ========================================
243
+ // Tuple Types
244
+ // ========================================
245
+
246
+ describe('Tuple Types', () => {
247
+ it('should handle simple tuple(string,uint8)', () => {
248
+ const builder = TagTypeBuilder.tuple({
249
+ name: TagTypeBuilder.string(),
250
+ age: TagTypeBuilder.uint8()
251
+ });
252
+ const typeBytes = '0x' + builder.getTypeBytes();
253
+ const abiType = builder.getAbiTypeString();
254
+ const fieldNames = builder.getFieldNames();
255
+
256
+ expect(abiType).toBe('tuple(string,uint8)');
257
+ expect(fieldNames).toEqual([['name', 'age']]);
258
+
259
+ const parsedType =
260
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
261
+ expect(parsedType).toBe('tuple(string,uint8)');
262
+
263
+ // Array format
264
+ const value = ['Alice', 30n];
265
+ const encoded = abiCoder.encode([abiType], [value]);
266
+ const decoded = abiCoder.decode([abiType], encoded);
267
+ expect(decoded[0]).toEqual(value);
268
+ });
269
+
270
+ it('should handle tuple(address,uint256,bool)', () => {
271
+ const builder = TagTypeBuilder.tuple({
272
+ owner: TagTypeBuilder.address(),
273
+ balance: TagTypeBuilder.uint256(),
274
+ active: TagTypeBuilder.bool()
275
+ });
276
+ const abiType = builder.getAbiTypeString();
277
+ const fieldNames = builder.getFieldNames();
278
+
279
+ expect(abiType).toBe('tuple(address,uint256,bool)');
280
+ expect(fieldNames).toEqual([['owner', 'balance', 'active']]);
281
+
282
+ const value = [
283
+ '0x742d35cc6634c0532925a3b844bc9e7595f0beb0',
284
+ 1000n,
285
+ true
286
+ ];
287
+ const encoded = abiCoder.encode([abiType], [value]);
288
+ const decoded = abiCoder.decode([abiType], encoded);
289
+ expect((decoded[0][0] as string).toLowerCase()).toBe(
290
+ (value[0] as string).toLocaleLowerCase()
291
+ );
292
+ expect(decoded[0][1]).toBe(value[1]);
293
+ expect(decoded[0][2]).toBe(value[2]);
294
+ });
295
+
296
+ it('should handle nested tuple(string,tuple(uint8,bool))', () => {
297
+ const builder = TagTypeBuilder.tuple({
298
+ name: TagTypeBuilder.string(),
299
+ profile: TagTypeBuilder.tuple({
300
+ age: TagTypeBuilder.uint8(),
301
+ verified: TagTypeBuilder.bool()
302
+ })
303
+ });
304
+ const typeBytes = '0x' + builder.getTypeBytes();
305
+ const abiType = builder.getAbiTypeString();
306
+ const fieldNames = builder.getFieldNames();
307
+
308
+ expect(abiType).toBe('tuple(string,tuple(uint8,bool))');
309
+ expect(fieldNames).toEqual([
310
+ ['name', 'profile'],
311
+ ['age', 'verified']
312
+ ]);
313
+
314
+ const parsedType =
315
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
316
+ expect(parsedType).toBe('tuple(string,tuple(uint8,bool))');
317
+
318
+ const value = ['Bob', [25n, false]];
319
+ const encoded = abiCoder.encode([abiType], [value]);
320
+ const decoded = abiCoder.decode([abiType], encoded);
321
+ expect(decoded[0]).toEqual(value);
322
+ });
323
+
324
+ it('should handle deeply nested tuple', () => {
325
+ const builder = TagTypeBuilder.tuple({
326
+ id: TagTypeBuilder.uint256(),
327
+ data: TagTypeBuilder.tuple({
328
+ name: TagTypeBuilder.string(),
329
+ meta: TagTypeBuilder.tuple({
330
+ created: TagTypeBuilder.uint256(),
331
+ active: TagTypeBuilder.bool()
332
+ })
333
+ })
334
+ });
335
+ const abiType = builder.getAbiTypeString();
336
+ const fieldNames = builder.getFieldNames();
337
+
338
+ expect(abiType).toBe(
339
+ 'tuple(uint256,tuple(string,tuple(uint256,bool)))'
340
+ );
341
+ expect(fieldNames).toEqual([
342
+ ['id', 'data'],
343
+ ['name', 'meta'],
344
+ ['created', 'active']
345
+ ]);
346
+
347
+ const value = [1n, ['Test', [1234567890n, true]]];
348
+ const encoded = abiCoder.encode([abiType], [value]);
349
+ const decoded = abiCoder.decode([abiType], encoded);
350
+ expect(decoded[0]).toEqual(value);
351
+ });
352
+ });
353
+
354
+ // ========================================
355
+ // Array of Tuples
356
+ // ========================================
357
+
358
+ describe('Array of Tuples', () => {
359
+ it('should handle tuple(string,uint8)[] type', () => {
360
+ const builder = TagTypeBuilder.array(
361
+ TagTypeBuilder.tuple({
362
+ name: TagTypeBuilder.string(),
363
+ age: TagTypeBuilder.uint8()
364
+ })
365
+ );
366
+ const typeBytes = '0x' + builder.getTypeBytes();
367
+ const abiType = builder.getAbiTypeString();
368
+ const fieldNames = builder.getFieldNames();
369
+
370
+ expect(abiType).toBe('tuple(string,uint8)[]');
371
+ expect(fieldNames).toEqual([['name', 'age']]);
372
+
373
+ const parsedType =
374
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
375
+ expect(parsedType).toBe('tuple(string,uint8)[]');
376
+
377
+ const value = [
378
+ ['Alice', 30n],
379
+ ['Bob', 25n],
380
+ ['Charlie', 35n]
381
+ ];
382
+ const encoded = abiCoder.encode([abiType], [value]);
383
+ const decoded = abiCoder.decode([abiType], encoded);
384
+ expect(decoded[0]).toEqual(value);
385
+ });
386
+
387
+ it('should handle nested tuple array', () => {
388
+ const builder = TagTypeBuilder.array(
389
+ TagTypeBuilder.tuple({
390
+ id: TagTypeBuilder.uint256(),
391
+ data: TagTypeBuilder.tuple({
392
+ name: TagTypeBuilder.string(),
393
+ active: TagTypeBuilder.bool()
394
+ })
395
+ })
396
+ );
397
+ const abiType = builder.getAbiTypeString();
398
+ const fieldNames = builder.getFieldNames();
399
+
400
+ expect(abiType).toBe('tuple(uint256,tuple(string,bool))[]');
401
+ expect(fieldNames).toEqual([
402
+ ['id', 'data'],
403
+ ['name', 'active']
404
+ ]);
405
+
406
+ const value = [
407
+ [1n, ['Item1', true]],
408
+ [2n, ['Item2', false]]
409
+ ];
410
+ const encoded = abiCoder.encode([abiType], [value]);
411
+ const decoded = abiCoder.decode([abiType], encoded);
412
+ expect(decoded[0]).toEqual(value);
413
+ });
414
+ });
415
+
416
+ // ========================================
417
+ // Complex Mixed Types
418
+ // ========================================
419
+
420
+ describe('Complex Mixed Types', () => {
421
+ it('should handle tuple with array field', () => {
422
+ const builder = TagTypeBuilder.tuple({
423
+ name: TagTypeBuilder.string(),
424
+ tags: TagTypeBuilder.stringArray(),
425
+ count: TagTypeBuilder.uint256()
426
+ });
427
+ const abiType = builder.getAbiTypeString();
428
+
429
+ expect(abiType).toBe('tuple(string,string[],uint256)');
430
+
431
+ const value = ['Project', ['tag1', 'tag2', 'tag3'], 100n];
432
+ const encoded = abiCoder.encode([abiType], [value]);
433
+ const decoded = abiCoder.decode([abiType], encoded);
434
+ expect(decoded[0]).toEqual(value);
435
+ });
436
+
437
+ it('should handle tuple with multiple nested structures', () => {
438
+ const builder = TagTypeBuilder.tuple({
439
+ owner: TagTypeBuilder.address(),
440
+ metadata: TagTypeBuilder.tuple({
441
+ name: TagTypeBuilder.string(),
442
+ tags: TagTypeBuilder.stringArray()
443
+ }),
444
+ settings: TagTypeBuilder.tuple({
445
+ enabled: TagTypeBuilder.bool(),
446
+ maxValue: TagTypeBuilder.uint256()
447
+ })
448
+ });
449
+ const abiType = builder.getAbiTypeString();
450
+ const fieldNames = builder.getFieldNames();
451
+
452
+ expect(abiType).toBe(
453
+ 'tuple(address,tuple(string,string[]),tuple(bool,uint256))'
454
+ );
455
+ expect(fieldNames.length).toBe(3);
456
+
457
+ const value = [
458
+ '0x742d35cc6634c0532925a3b844bc9e7595f0beb0',
459
+ ['MyProject', ['web3', 'defi']],
460
+ [true, 1000n]
461
+ ];
462
+ const encoded = abiCoder.encode([abiType], [value]);
463
+ const decoded = abiCoder.decode([abiType], encoded);
464
+ expect((decoded[0][0] as string).toLowerCase()).toBe(
465
+ (value[0] as string).toLowerCase()
466
+ );
467
+ expect(decoded[0][1]).toEqual(value[1]);
468
+ expect(decoded[0][2]).toEqual(value[2]);
469
+ });
470
+ });
471
+
472
+ // ========================================
473
+ // Type Bytes Parsing
474
+ // ========================================
475
+
476
+ describe('Type Bytes Parsing', () => {
477
+ it('should parse simple type bytes correctly', () => {
478
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x03')).toBe(
479
+ 'string'
480
+ );
481
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x0101')).toBe(
482
+ 'uint8'
483
+ );
484
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x0120')).toBe(
485
+ 'uint256'
486
+ );
487
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x02')).toBe(
488
+ 'bool'
489
+ );
490
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x07')).toBe(
491
+ 'address'
492
+ );
493
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x09')).toBe(
494
+ 'bytes'
495
+ );
496
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x0820')).toBe(
497
+ 'bytes32'
498
+ );
499
+ });
500
+
501
+ it('should parse array type bytes correctly', () => {
502
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x0403')).toBe(
503
+ 'string[]'
504
+ );
505
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x0407')).toBe(
506
+ 'address[]'
507
+ );
508
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x040120')).toBe(
509
+ 'uint256[]'
510
+ );
511
+ });
512
+
513
+ it('should parse fixed array type bytes correctly', () => {
514
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x05000303')).toBe(
515
+ 'string[3]'
516
+ );
517
+ expect(TagTypeBuilder.parseTypeBytesToAbiString('0x05000507')).toBe(
518
+ 'address[5]'
519
+ );
520
+ });
521
+
522
+ it('should parse simple tuple type bytes correctly', () => {
523
+ // tuple(string,uint8)
524
+ expect(
525
+ TagTypeBuilder.parseTypeBytesToAbiString('0x060002030101')
526
+ ).toBe('tuple(string,uint8)');
527
+ });
528
+
529
+ it('should parse complex tuple type bytes correctly', () => {
530
+ // tuple(string,uint8,address[],bool) - from tag-management.ts
531
+ expect(
532
+ TagTypeBuilder.parseTypeBytesToAbiString('0x060004030101040702')
533
+ ).toBe('tuple(string,uint8,address[],bool)');
534
+ });
535
+
536
+ it('should parse nested tuple type bytes correctly', () => {
537
+ // Build nested tuple and verify parsing
538
+ const builder = TagTypeBuilder.tuple({
539
+ name: TagTypeBuilder.string(),
540
+ profile: TagTypeBuilder.tuple({
541
+ age: TagTypeBuilder.uint8(),
542
+ active: TagTypeBuilder.bool()
543
+ })
544
+ });
545
+ const typeBytes = '0x' + builder.getTypeBytes();
546
+ const parsed = TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
547
+ expect(parsed).toBe('tuple(string,tuple(uint8,bool))');
548
+ });
549
+
550
+ it('should roundtrip: build -> parse -> build', () => {
551
+ const original = TagTypeBuilder.tuple({
552
+ id: TagTypeBuilder.uint256(),
553
+ data: TagTypeBuilder.stringArray(),
554
+ owner: TagTypeBuilder.address()
555
+ });
556
+
557
+ const typeBytes = '0x' + original.getTypeBytes();
558
+ const parsedAbi =
559
+ TagTypeBuilder.parseTypeBytesToAbiString(typeBytes);
560
+
561
+ expect(parsedAbi).toBe('tuple(uint256,string[],address)');
562
+ expect(parsedAbi).toBe(original.getAbiTypeString());
563
+ });
564
+ });
565
+
566
+ // ========================================
567
+ // Object to Array Conversion
568
+ // ========================================
569
+
570
+ describe('Object to Array Conversion', () => {
571
+ it('should return non-object values as-is', () => {
572
+ expect(TagTypeBuilder.convertObjectToArray('string', 'hello')).toBe(
573
+ 'hello'
574
+ );
575
+ expect(TagTypeBuilder.convertObjectToArray('uint256', 123n)).toBe(
576
+ 123n
577
+ );
578
+ expect(TagTypeBuilder.convertObjectToArray('bool', true)).toBe(
579
+ true
580
+ );
581
+ expect(TagTypeBuilder.convertObjectToArray('address', null)).toBe(
582
+ null
583
+ );
584
+ });
585
+
586
+ it('should return arrays as-is', () => {
587
+ const arr = ['Alice', 30];
588
+ expect(
589
+ TagTypeBuilder.convertObjectToArray('tuple(string,uint8)', arr)
590
+ ).toBe(arr);
591
+ });
592
+
593
+ it('should return non-tuple types as-is', () => {
594
+ const obj = { foo: 'bar' };
595
+ expect(TagTypeBuilder.convertObjectToArray('string', obj)).toBe(
596
+ obj
597
+ );
598
+ });
599
+
600
+ it('should convert simple tuple object to array', () => {
601
+ const obj = { name: 'Alice', age: 30 };
602
+ const result = TagTypeBuilder.convertObjectToArray(
603
+ 'tuple(string,uint8)',
604
+ obj
605
+ );
606
+ expect(result).toEqual(['Alice', 30]);
607
+ });
608
+
609
+ it('should convert nested tuple object to array', () => {
610
+ const obj = {
611
+ name: 'Bob',
612
+ profile: {
613
+ age: 25,
614
+ verified: true
615
+ }
616
+ };
617
+ const result = TagTypeBuilder.convertObjectToArray(
618
+ 'tuple(string,tuple(uint8,bool))',
619
+ obj
620
+ );
621
+ expect(result).toEqual(['Bob', [25, true]]);
622
+ });
623
+
624
+ it('should convert deeply nested tuple object to array', () => {
625
+ const obj = {
626
+ id: 1,
627
+ data: {
628
+ name: 'Test',
629
+ meta: {
630
+ created: 1234567890,
631
+ active: true
632
+ }
633
+ }
634
+ };
635
+ const result = TagTypeBuilder.convertObjectToArray(
636
+ 'tuple(uint256,tuple(string,tuple(uint256,bool)))',
637
+ obj
638
+ );
639
+ expect(result).toEqual([1, ['Test', [1234567890, true]]]);
640
+ });
641
+
642
+ it('should handle tuple with mixed types', () => {
643
+ const obj = {
644
+ owner: '0x1234567890123456789012345678901234567890',
645
+ balance: 1000n,
646
+ active: true
647
+ };
648
+ const result = TagTypeBuilder.convertObjectToArray(
649
+ 'tuple(address,uint256,bool)',
650
+ obj
651
+ );
652
+ expect(result).toEqual([
653
+ '0x1234567890123456789012345678901234567890',
654
+ 1000n,
655
+ true
656
+ ]);
657
+ });
658
+
659
+ it('should throw error on field count mismatch', () => {
660
+ const obj = { name: 'Alice' }; // Missing 'age' field
661
+ expect(() => {
662
+ TagTypeBuilder.convertObjectToArray('tuple(string,uint8)', obj);
663
+ }).toThrow('Tuple field count mismatch');
664
+ });
665
+
666
+ it('should convert tuple array (tuple[]) correctly', () => {
667
+ const objArray = [
668
+ {
669
+ algorithm: 0,
670
+ addr: '0x742d35cc6634c0532925a3b844bc9e7595f0beb0'
671
+ },
672
+ {
673
+ algorithm: 1,
674
+ addr: '0x1111111111111111111111111111111111111111'
675
+ }
676
+ ];
677
+ const result = TagTypeBuilder.convertObjectToArray(
678
+ 'tuple(uint8,address)[]',
679
+ objArray
680
+ );
681
+ expect(result).toEqual([
682
+ [0, '0x742d35cc6634c0532925a3b844bc9e7595f0beb0'],
683
+ [1, '0x1111111111111111111111111111111111111111']
684
+ ]);
685
+ });
686
+
687
+ it('should handle nested tuple within tuple array', () => {
688
+ const objArray = [
689
+ {
690
+ id: 1,
691
+ data: {
692
+ name: 'Alice',
693
+ active: true
694
+ }
695
+ },
696
+ {
697
+ id: 2,
698
+ data: {
699
+ name: 'Bob',
700
+ active: false
701
+ }
702
+ }
703
+ ];
704
+ const result = TagTypeBuilder.convertObjectToArray(
705
+ 'tuple(uint256,tuple(string,bool))[]',
706
+ objArray
707
+ );
708
+ expect(result).toEqual([
709
+ [1, ['Alice', true]],
710
+ [2, ['Bob', false]]
711
+ ]);
712
+ });
713
+
714
+ it('should handle empty tuple array', () => {
715
+ const objArray: any[] = [];
716
+ const result = TagTypeBuilder.convertObjectToArray(
717
+ 'tuple(uint8,address)[]',
718
+ objArray
719
+ );
720
+ expect(result).toEqual([]);
721
+ });
722
+ });
723
+
724
+ // ========================================
725
+ // Parse Tuple Field Types
726
+ // ========================================
727
+
728
+ describe('Parse Tuple Field Types', () => {
729
+ it('should parse simple tuple fields', () => {
730
+ const fields = TagTypeBuilder.parseTupleFieldTypes(
731
+ 'tuple(string,uint8)'
732
+ );
733
+ expect(fields).toEqual(['string', 'uint8']);
734
+ });
735
+
736
+ it('should parse tuple with multiple fields', () => {
737
+ const fields = TagTypeBuilder.parseTupleFieldTypes(
738
+ 'tuple(address,uint256,bool,string)'
739
+ );
740
+ expect(fields).toEqual(['address', 'uint256', 'bool', 'string']);
741
+ });
742
+
743
+ it('should parse nested tuple fields', () => {
744
+ const fields = TagTypeBuilder.parseTupleFieldTypes(
745
+ 'tuple(string,tuple(uint8,bool))'
746
+ );
747
+ expect(fields).toEqual(['string', 'tuple(uint8,bool)']);
748
+ });
749
+
750
+ it('should parse deeply nested tuple fields', () => {
751
+ const fields = TagTypeBuilder.parseTupleFieldTypes(
752
+ 'tuple(uint256,tuple(string,tuple(uint256,bool)))'
753
+ );
754
+ expect(fields).toEqual([
755
+ 'uint256',
756
+ 'tuple(string,tuple(uint256,bool))'
757
+ ]);
758
+ });
759
+
760
+ it('should parse tuple with array fields', () => {
761
+ const fields = TagTypeBuilder.parseTupleFieldTypes(
762
+ 'tuple(string,string[],uint256)'
763
+ );
764
+ expect(fields).toEqual(['string', 'string[]', 'uint256']);
765
+ });
766
+
767
+ it('should return empty array for non-tuple types', () => {
768
+ expect(TagTypeBuilder.parseTupleFieldTypes('string')).toEqual([]);
769
+ expect(TagTypeBuilder.parseTupleFieldTypes('uint256')).toEqual([]);
770
+ expect(TagTypeBuilder.parseTupleFieldTypes('address[]')).toEqual(
771
+ []
772
+ );
773
+ });
774
+ });
775
+
776
+ // ========================================
777
+ // Edge Cases
778
+ // ========================================
779
+
780
+ describe('Edge Cases', () => {
781
+ it('should handle empty string', () => {
782
+ const builder = TagTypeBuilder.string();
783
+ const abiType = builder.getAbiTypeString();
784
+
785
+ const value = '';
786
+ const encoded = abiCoder.encode([abiType], [value]);
787
+ const decoded = abiCoder.decode([abiType], encoded);
788
+ expect(decoded[0]).toBe(value);
789
+ });
790
+
791
+ it('should handle empty array', () => {
792
+ const builder = TagTypeBuilder.stringArray();
793
+ const abiType = builder.getAbiTypeString();
794
+
795
+ const value: string[] = [];
796
+ const encoded = abiCoder.encode([abiType], [value]);
797
+ const decoded = abiCoder.decode([abiType], encoded);
798
+ expect(decoded[0]).toEqual(value);
799
+ });
800
+
801
+ it('should handle zero values', () => {
802
+ const builder = TagTypeBuilder.uint256();
803
+ const abiType = builder.getAbiTypeString();
804
+
805
+ const value = 0n;
806
+ const encoded = abiCoder.encode([abiType], [value]);
807
+ const decoded = abiCoder.decode([abiType], encoded);
808
+ expect(decoded[0]).toBe(value);
809
+ });
810
+
811
+ it('should handle max uint256', () => {
812
+ const builder = TagTypeBuilder.uint256();
813
+ const abiType = builder.getAbiTypeString();
814
+
815
+ const value = 2n ** 256n - 1n;
816
+ const encoded = abiCoder.encode([abiType], [value]);
817
+ const decoded = abiCoder.decode([abiType], encoded);
818
+ expect(decoded[0]).toBe(value);
819
+ });
820
+ });
821
+ });