@beclab/olaresid 0.1.1 → 0.1.3
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/CLI.md +1300 -0
- package/README.md +40 -31
- package/TAG.md +589 -0
- package/dist/abi/RootResolver2ABI.d.ts +54 -0
- package/dist/abi/RootResolver2ABI.d.ts.map +1 -0
- package/dist/abi/RootResolver2ABI.js +240 -0
- package/dist/abi/RootResolver2ABI.js.map +1 -0
- package/dist/business/index.d.ts +302 -0
- package/dist/business/index.d.ts.map +1 -0
- package/dist/business/index.js +1211 -0
- package/dist/business/index.js.map +1 -0
- package/dist/business/tag-context.d.ts +219 -0
- package/dist/business/tag-context.d.ts.map +1 -0
- package/dist/business/tag-context.js +560 -0
- package/dist/business/tag-context.js.map +1 -0
- package/dist/cli.js +2102 -39
- package/dist/cli.js.map +1 -1
- package/dist/debug.d.ts.map +1 -1
- package/dist/debug.js +14 -2
- package/dist/debug.js.map +1 -1
- package/dist/index.d.ts +51 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +241 -12
- package/dist/index.js.map +1 -1
- package/dist/utils/crypto-utils.d.ts +130 -0
- package/dist/utils/crypto-utils.d.ts.map +1 -0
- package/dist/utils/crypto-utils.js +402 -0
- package/dist/utils/crypto-utils.js.map +1 -0
- package/dist/utils/error-parser.d.ts +35 -0
- package/dist/utils/error-parser.d.ts.map +1 -0
- package/dist/utils/error-parser.js +202 -0
- package/dist/utils/error-parser.js.map +1 -0
- package/dist/utils/olares-id.d.ts +36 -0
- package/dist/utils/olares-id.d.ts.map +1 -0
- package/dist/utils/olares-id.js +52 -0
- package/dist/utils/olares-id.js.map +1 -0
- package/dist/utils/tag-abi-codec.d.ts +69 -0
- package/dist/utils/tag-abi-codec.d.ts.map +1 -0
- package/dist/utils/tag-abi-codec.js +144 -0
- package/dist/utils/tag-abi-codec.js.map +1 -0
- package/dist/utils/tag-type-builder.d.ts +158 -0
- package/dist/utils/tag-type-builder.d.ts.map +1 -0
- package/dist/utils/tag-type-builder.js +410 -0
- package/dist/utils/tag-type-builder.js.map +1 -0
- package/examples/crypto-utilities.ts +140 -0
- package/examples/domain-context.ts +80 -0
- package/examples/generate-mnemonic.ts +149 -0
- package/examples/index.ts +1 -1
- package/examples/ip.ts +171 -0
- package/examples/legacy.ts +10 -10
- package/examples/list-wallets.ts +81 -0
- package/examples/olares-id-format.ts +197 -0
- package/examples/quasar-demo/.eslintrc.js +23 -0
- package/examples/quasar-demo/.quasar/app.js +43 -0
- package/examples/quasar-demo/.quasar/client-entry.js +38 -0
- package/examples/quasar-demo/.quasar/client-prefetch.js +130 -0
- package/examples/quasar-demo/.quasar/quasar-user-options.js +16 -0
- package/examples/quasar-demo/README.md +49 -0
- package/examples/quasar-demo/index.html +11 -0
- package/examples/quasar-demo/package-lock.json +6407 -0
- package/examples/quasar-demo/package.json +36 -0
- package/examples/quasar-demo/quasar.config.js +73 -0
- package/examples/quasar-demo/src/App.vue +13 -0
- package/examples/quasar-demo/src/css/app.scss +1 -0
- package/examples/quasar-demo/src/layouts/MainLayout.vue +21 -0
- package/examples/quasar-demo/src/pages/IndexPage.vue +905 -0
- package/examples/quasar-demo/src/router/index.ts +25 -0
- package/examples/quasar-demo/src/router/routes.ts +11 -0
- package/examples/quasar-demo/tsconfig.json +28 -0
- package/examples/register-subdomain.ts +152 -0
- package/examples/rsa-keypair.ts +148 -0
- package/examples/tag-builder.ts +235 -0
- package/examples/tag-management.ts +534 -0
- package/examples/tag-nested-tuple.ts +190 -0
- package/examples/tag-simple.ts +149 -0
- package/examples/tag-tagger.ts +217 -0
- package/examples/test-nested-tuple-conversion.ts +143 -0
- package/examples/test-type-bytes-parser.ts +70 -0
- package/examples/transfer-domain.ts +197 -0
- package/examples/wallet-management.ts +196 -0
- package/package.json +24 -15
- package/src/abi/RootResolver2ABI.ts +237 -0
- package/src/business/index.ts +1492 -0
- package/src/business/tag-context.ts +747 -0
- package/src/cli.ts +2772 -39
- package/src/debug.ts +17 -2
- package/src/index.ts +313 -17
- package/src/utils/crypto-utils.ts +459 -0
- package/src/utils/error-parser.ts +225 -0
- package/src/utils/olares-id.ts +49 -0
- package/src/utils/tag-abi-codec.ts +158 -0
- package/src/utils/tag-type-builder.ts +469 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
import { DIDConsole } from '../index';
|
|
2
|
+
import { TransactionResult } from './index';
|
|
3
|
+
import { parseContractError } from '../utils/error-parser';
|
|
4
|
+
import { TagTypeBuilder } from '../utils/tag-type-builder';
|
|
5
|
+
import { TagAbiCodec } from '../utils/tag-abi-codec';
|
|
6
|
+
import { normalizeToDomain } from '../utils/olares-id';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tag Operation Context
|
|
10
|
+
* Provides complete Tag management functionality
|
|
11
|
+
*/
|
|
12
|
+
export class TagContext {
|
|
13
|
+
constructor(private console: DIDConsole, private fromDomain: string) {
|
|
14
|
+
// Support Olares ID format (user@domain.com)
|
|
15
|
+
this.fromDomain = normalizeToDomain(fromDomain);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ========================================
|
|
19
|
+
// 1. Tag Type Definition Management
|
|
20
|
+
// ========================================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Set the tagger (manager) for a Tag
|
|
24
|
+
* Only the domain owner can set the tagger
|
|
25
|
+
*
|
|
26
|
+
* @param tagName Tag name
|
|
27
|
+
* @param taggerAddress Address of the tagger (manager)
|
|
28
|
+
* @returns Transaction result
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // Set a specific address as the tagger
|
|
32
|
+
* await tagCtx.setTagger('email', '0x1234...');
|
|
33
|
+
*
|
|
34
|
+
* // Set zero address to allow anyone to manage
|
|
35
|
+
* await tagCtx.setTagger('email', '0x0000000000000000000000000000000000000000');
|
|
36
|
+
*/
|
|
37
|
+
async setTagger(
|
|
38
|
+
tagName: string,
|
|
39
|
+
taggerAddress: string
|
|
40
|
+
): Promise<TransactionResult> {
|
|
41
|
+
const contract = this.console.getSignerContractDID();
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const tx = await contract.setTagger(
|
|
45
|
+
this.fromDomain,
|
|
46
|
+
tagName,
|
|
47
|
+
taggerAddress
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const receipt = await tx.wait();
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
success: true,
|
|
54
|
+
transactionHash: receipt.hash,
|
|
55
|
+
gasUsed: receipt.gasUsed,
|
|
56
|
+
blockNumber: receipt.blockNumber
|
|
57
|
+
};
|
|
58
|
+
} catch (error: any) {
|
|
59
|
+
const errorInfo = parseContractError(error);
|
|
60
|
+
if (errorInfo.isNetworkError) {
|
|
61
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`Failed to set tagger: ${errorInfo.message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the tagger (manager) address for a Tag
|
|
69
|
+
*
|
|
70
|
+
* @param tagName Tag name
|
|
71
|
+
* @returns Tagger address (returns zero address if no tagger is set)
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* const tagger = await tagCtx.getTagger('email');
|
|
75
|
+
* console.log('Tagger address:', tagger);
|
|
76
|
+
*
|
|
77
|
+
* if (tagger === '0x0000000000000000000000000000000000000000') {
|
|
78
|
+
* console.log('No specific tagger, anyone can manage');
|
|
79
|
+
* }
|
|
80
|
+
*/
|
|
81
|
+
async getTagger(tagName: string): Promise<string> {
|
|
82
|
+
const contract = this.console.getContractDID();
|
|
83
|
+
return await contract.getTagger(this.fromDomain, tagName);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Define a new Tag type
|
|
88
|
+
* @param tagName Tag name
|
|
89
|
+
* @param tagType Tag type object (built using TagTypeBuilder)
|
|
90
|
+
* @returns Transaction result
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* const emailType = TagTypeBuilder.string();
|
|
94
|
+
* await tagCtx.defineTag('email', emailType);
|
|
95
|
+
*/
|
|
96
|
+
async defineTag(
|
|
97
|
+
tagName: string,
|
|
98
|
+
tagType: TagTypeBuilder
|
|
99
|
+
): Promise<TransactionResult> {
|
|
100
|
+
const contract = this.console.getSignerContractDID();
|
|
101
|
+
|
|
102
|
+
// Get type bytes directly from TagTypeBuilder (no contract call needed)
|
|
103
|
+
const abiTypeBytes = '0x' + tagType.getTypeBytes();
|
|
104
|
+
|
|
105
|
+
// Get field names (for complex types)
|
|
106
|
+
const fieldNames = tagType.getFieldNames();
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const tx = await contract.defineTag(
|
|
110
|
+
this.fromDomain,
|
|
111
|
+
tagName,
|
|
112
|
+
abiTypeBytes,
|
|
113
|
+
fieldNames
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const receipt = await tx.wait();
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
success: true,
|
|
120
|
+
transactionHash: receipt.hash,
|
|
121
|
+
gasUsed: receipt.gasUsed,
|
|
122
|
+
blockNumber: receipt.blockNumber
|
|
123
|
+
};
|
|
124
|
+
} catch (error: any) {
|
|
125
|
+
const errorInfo = parseContractError(error);
|
|
126
|
+
if (errorInfo.isNetworkError) {
|
|
127
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
128
|
+
}
|
|
129
|
+
throw new Error(`Failed to define tag: ${errorInfo.message}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get Tag type definition
|
|
135
|
+
* @param tagName Tag name
|
|
136
|
+
* @returns Tag type information, or null if not found
|
|
137
|
+
*/
|
|
138
|
+
async getTagType(tagName: string): Promise<{
|
|
139
|
+
abiType: string;
|
|
140
|
+
fieldNamesHash: string[];
|
|
141
|
+
} | null> {
|
|
142
|
+
try {
|
|
143
|
+
const contract = this.console.getContractDID();
|
|
144
|
+
const [abiType, fieldNamesHash] = await contract.getTagType(
|
|
145
|
+
this.fromDomain,
|
|
146
|
+
tagName
|
|
147
|
+
);
|
|
148
|
+
return { abiType, fieldNamesHash };
|
|
149
|
+
} catch (error: any) {
|
|
150
|
+
const errorInfo = parseContractError(error);
|
|
151
|
+
if (errorInfo.errorName === 'UndefinedTag') {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get all Tag names defined by the domain
|
|
160
|
+
* @returns Array of Tag names
|
|
161
|
+
*/
|
|
162
|
+
async getDefinedTagNames(): Promise<string[]> {
|
|
163
|
+
const contract = this.console.getContractDID();
|
|
164
|
+
return await contract.getDefinedTagNames(this.fromDomain);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get the count of Tags defined by the domain
|
|
169
|
+
*/
|
|
170
|
+
async getDefinedTagCount(): Promise<number> {
|
|
171
|
+
const contract = this.console.getContractDID();
|
|
172
|
+
const count = await contract.getDefinedTagCount(this.fromDomain);
|
|
173
|
+
return Number(count);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ========================================
|
|
177
|
+
// 2. Tag Value Operations
|
|
178
|
+
// ========================================
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Set Tag value
|
|
182
|
+
* Supports simple values, arrays, and tuples (as objects or arrays)
|
|
183
|
+
* Objects are automatically converted to arrays based on ABI type structure
|
|
184
|
+
*
|
|
185
|
+
* @param toDomain Target domain
|
|
186
|
+
* @param tagName Tag name
|
|
187
|
+
* @param value Tag value (objects for tuples, arrays, or simple values)
|
|
188
|
+
* @returns Transaction result
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* // Simple value
|
|
192
|
+
* await tagCtx.setTag('example.com', 'email', 'user@example.com');
|
|
193
|
+
*
|
|
194
|
+
* // Array type
|
|
195
|
+
* await tagCtx.setTag('example.com', 'links', ['https://...', 'https://...']);
|
|
196
|
+
*
|
|
197
|
+
* // Tuple type - object notation (recommended)
|
|
198
|
+
* await tagCtx.setTag('example.com', 'userInfo', {
|
|
199
|
+
* name: 'Alice',
|
|
200
|
+
* age: 30,
|
|
201
|
+
* verified: true
|
|
202
|
+
* });
|
|
203
|
+
*
|
|
204
|
+
* // Tuple type - array notation (still supported)
|
|
205
|
+
* await tagCtx.setTag('example.com', 'userInfo', ['Alice', 30, true]);
|
|
206
|
+
*
|
|
207
|
+
* // Nested tuple - object notation
|
|
208
|
+
* await tagCtx.setTag('example.com', 'profile', {
|
|
209
|
+
* name: 'Alice',
|
|
210
|
+
* details: {
|
|
211
|
+
* age: 30,
|
|
212
|
+
* verified: true
|
|
213
|
+
* }
|
|
214
|
+
* });
|
|
215
|
+
*/
|
|
216
|
+
async setTag(
|
|
217
|
+
toDomain: string,
|
|
218
|
+
tagName: string,
|
|
219
|
+
value: any
|
|
220
|
+
): Promise<TransactionResult> {
|
|
221
|
+
// Support Olares ID format
|
|
222
|
+
toDomain = normalizeToDomain(toDomain);
|
|
223
|
+
|
|
224
|
+
const contract = this.console.getSignerContractDID();
|
|
225
|
+
|
|
226
|
+
// Get tag type
|
|
227
|
+
const tagTypeInfo = await this.getTagType(tagName);
|
|
228
|
+
if (!tagTypeInfo) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`Tag "${tagName}" is not defined in domain "${this.fromDomain}"`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// Parse type bytes to ABI type string
|
|
236
|
+
const abiType = TagTypeBuilder.parseTypeBytesToAbiString(
|
|
237
|
+
tagTypeInfo.abiType
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Encode the ENTIRE value using TagAbiCodec
|
|
241
|
+
// Objects are automatically converted to arrays based on ABI type structure
|
|
242
|
+
const encodedValue = TagAbiCodec.encode(abiType, value);
|
|
243
|
+
|
|
244
|
+
// Check if tag already exists
|
|
245
|
+
const hasTag = await contract.hasTag(
|
|
246
|
+
this.fromDomain,
|
|
247
|
+
toDomain,
|
|
248
|
+
tagName
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
let tx;
|
|
252
|
+
if (hasTag) {
|
|
253
|
+
// Update tag value
|
|
254
|
+
tx = await contract.updateTagElem(
|
|
255
|
+
this.fromDomain,
|
|
256
|
+
toDomain,
|
|
257
|
+
tagName,
|
|
258
|
+
[], // Empty path means root element
|
|
259
|
+
encodedValue
|
|
260
|
+
);
|
|
261
|
+
} else {
|
|
262
|
+
// Add new tag
|
|
263
|
+
tx = await contract.addTag(
|
|
264
|
+
this.fromDomain,
|
|
265
|
+
toDomain,
|
|
266
|
+
tagName,
|
|
267
|
+
encodedValue
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const receipt = await tx.wait();
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
success: true,
|
|
275
|
+
transactionHash: receipt.hash,
|
|
276
|
+
gasUsed: receipt.gasUsed,
|
|
277
|
+
blockNumber: receipt.blockNumber
|
|
278
|
+
};
|
|
279
|
+
} catch (error: any) {
|
|
280
|
+
const errorInfo = parseContractError(error);
|
|
281
|
+
if (errorInfo.isNetworkError) {
|
|
282
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
283
|
+
}
|
|
284
|
+
throw new Error(`Failed to set tag: ${errorInfo.message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get Tag value
|
|
290
|
+
* @param toDomain Target domain
|
|
291
|
+
* @param tagName Tag name
|
|
292
|
+
* @returns Tag value, or null if not found
|
|
293
|
+
*/
|
|
294
|
+
async getTag(toDomain: string, tagName: string): Promise<any | null> {
|
|
295
|
+
// Support Olares ID format
|
|
296
|
+
toDomain = normalizeToDomain(toDomain);
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
const contract = this.console.getContractDID();
|
|
300
|
+
|
|
301
|
+
// Get tag type
|
|
302
|
+
const tagTypeInfo = await this.getTagType(tagName);
|
|
303
|
+
if (!tagTypeInfo) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Get encoded value
|
|
308
|
+
const encodedValue = await contract.getTagElem(
|
|
309
|
+
this.fromDomain,
|
|
310
|
+
toDomain,
|
|
311
|
+
tagName,
|
|
312
|
+
[]
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
// Parse type and decode
|
|
316
|
+
const abiType = TagTypeBuilder.parseTypeBytesToAbiString(
|
|
317
|
+
tagTypeInfo.abiType
|
|
318
|
+
);
|
|
319
|
+
return TagAbiCodec.decode(abiType, encodedValue);
|
|
320
|
+
} catch (error: any) {
|
|
321
|
+
const errorInfo = parseContractError(error);
|
|
322
|
+
if (
|
|
323
|
+
errorInfo.errorName === 'TagInvalidOp' ||
|
|
324
|
+
errorInfo.errorName === 'UndefinedTag'
|
|
325
|
+
) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
if (errorInfo.isNetworkError) {
|
|
329
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
330
|
+
}
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Remove Tag
|
|
337
|
+
* @param toDomain Target domain
|
|
338
|
+
* @param tagName Tag name
|
|
339
|
+
* @returns Transaction result
|
|
340
|
+
*/
|
|
341
|
+
async removeTag(
|
|
342
|
+
toDomain: string,
|
|
343
|
+
tagName: string
|
|
344
|
+
): Promise<TransactionResult> {
|
|
345
|
+
// Support Olares ID format
|
|
346
|
+
toDomain = normalizeToDomain(toDomain);
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
const contract = this.console.getSignerContractDID();
|
|
350
|
+
const tx = await contract.removeTag(
|
|
351
|
+
this.fromDomain,
|
|
352
|
+
toDomain,
|
|
353
|
+
tagName
|
|
354
|
+
);
|
|
355
|
+
const receipt = await tx.wait();
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
success: true,
|
|
359
|
+
transactionHash: receipt.hash,
|
|
360
|
+
gasUsed: receipt.gasUsed,
|
|
361
|
+
blockNumber: receipt.blockNumber
|
|
362
|
+
};
|
|
363
|
+
} catch (error: any) {
|
|
364
|
+
const errorInfo = parseContractError(error);
|
|
365
|
+
if (errorInfo.isNetworkError) {
|
|
366
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
367
|
+
}
|
|
368
|
+
throw new Error(`Failed to remove tag: ${errorInfo.message}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Check if Tag exists
|
|
374
|
+
* @param toDomain Target domain
|
|
375
|
+
* @param tagName Tag name
|
|
376
|
+
* @returns Whether the tag exists
|
|
377
|
+
*/
|
|
378
|
+
async hasTag(toDomain: string, tagName: string): Promise<boolean> {
|
|
379
|
+
// Support Olares ID format
|
|
380
|
+
toDomain = normalizeToDomain(toDomain);
|
|
381
|
+
|
|
382
|
+
const contract = this.console.getContractDID();
|
|
383
|
+
return await contract.hasTag(this.fromDomain, toDomain, tagName);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Get all Tag names for the domain
|
|
388
|
+
* @param toDomain Target domain
|
|
389
|
+
* @returns Array of Tag names
|
|
390
|
+
*/
|
|
391
|
+
async getTagNames(toDomain: string): Promise<string[]> {
|
|
392
|
+
// Support Olares ID format
|
|
393
|
+
toDomain = normalizeToDomain(toDomain);
|
|
394
|
+
|
|
395
|
+
const contract = this.console.getContractDID();
|
|
396
|
+
const count = await contract.getTagCount(this.fromDomain, toDomain);
|
|
397
|
+
const names: string[] = [];
|
|
398
|
+
|
|
399
|
+
for (let i = 0; i < count; i++) {
|
|
400
|
+
const name = await contract.getTagNameByIndex(
|
|
401
|
+
this.fromDomain,
|
|
402
|
+
toDomain,
|
|
403
|
+
i
|
|
404
|
+
);
|
|
405
|
+
names.push(name);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return names;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Get all Tags and their values for the domain
|
|
413
|
+
* @param toDomain Target domain
|
|
414
|
+
* @returns Array of Tags, each containing name and value
|
|
415
|
+
*/
|
|
416
|
+
async getAllTags(
|
|
417
|
+
toDomain: string
|
|
418
|
+
): Promise<Array<{ name: string; value: any }>> {
|
|
419
|
+
const names = await this.getTagNames(toDomain);
|
|
420
|
+
const result = [];
|
|
421
|
+
|
|
422
|
+
for (const name of names) {
|
|
423
|
+
const value = await this.getTag(toDomain, name);
|
|
424
|
+
result.push({ name, value });
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ========================================
|
|
431
|
+
// 3. Array Operations (Advanced Features)
|
|
432
|
+
// ========================================
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Add an element to an array Tag
|
|
436
|
+
* @param toDomain Target domain
|
|
437
|
+
* @param tagName Tag name
|
|
438
|
+
* @param value Element value to add
|
|
439
|
+
* @param elemPath Element path (for nested arrays), defaults to empty array
|
|
440
|
+
* @returns Transaction result
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* // Add element to a 1D array
|
|
444
|
+
* await tagCtx.pushElement('example.com', 'socialLinks', 'https://twitter.com/user');
|
|
445
|
+
*
|
|
446
|
+
* // Add element to the first sub-array of a 2D array
|
|
447
|
+
* await tagCtx.pushElement('example.com', 'matrix', 'value', [0]);
|
|
448
|
+
*/
|
|
449
|
+
async pushElement(
|
|
450
|
+
toDomain: string,
|
|
451
|
+
tagName: string,
|
|
452
|
+
value: any,
|
|
453
|
+
elemPath: number[] = []
|
|
454
|
+
): Promise<TransactionResult> {
|
|
455
|
+
// Support Olares ID format
|
|
456
|
+
toDomain = normalizeToDomain(toDomain);
|
|
457
|
+
|
|
458
|
+
const contract = this.console.getSignerContractDID();
|
|
459
|
+
|
|
460
|
+
// Get tag type and encode element
|
|
461
|
+
const tagTypeInfo = await this.getTagType(tagName);
|
|
462
|
+
if (!tagTypeInfo) {
|
|
463
|
+
throw new Error(`Tag "${tagName}" is not defined`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
// Parse type bytes to ABI string
|
|
468
|
+
const rootAbiType = TagTypeBuilder.parseTypeBytesToAbiString(
|
|
469
|
+
tagTypeInfo.abiType
|
|
470
|
+
);
|
|
471
|
+
// Get ELEMENT type (not root type!)
|
|
472
|
+
const elementAbiType = this.getElementAbiType(
|
|
473
|
+
rootAbiType,
|
|
474
|
+
elemPath
|
|
475
|
+
);
|
|
476
|
+
// Encode the SINGLE ELEMENT (not the whole array)
|
|
477
|
+
const encodedValue = TagAbiCodec.encode(elementAbiType, value);
|
|
478
|
+
|
|
479
|
+
const tx = await contract.pushTagElem(
|
|
480
|
+
this.fromDomain,
|
|
481
|
+
toDomain,
|
|
482
|
+
tagName,
|
|
483
|
+
elemPath,
|
|
484
|
+
encodedValue
|
|
485
|
+
);
|
|
486
|
+
const receipt = await tx.wait();
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
success: true,
|
|
490
|
+
transactionHash: receipt.hash,
|
|
491
|
+
gasUsed: receipt.gasUsed,
|
|
492
|
+
blockNumber: receipt.blockNumber
|
|
493
|
+
};
|
|
494
|
+
} catch (error: any) {
|
|
495
|
+
const errorInfo = parseContractError(error);
|
|
496
|
+
if (errorInfo.isNetworkError) {
|
|
497
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
498
|
+
}
|
|
499
|
+
throw new Error(`Failed to push element: ${errorInfo.message}`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Remove the last element from an array Tag
|
|
505
|
+
* @param toDomain Target domain
|
|
506
|
+
* @param tagName Tag name
|
|
507
|
+
* @param elemPath Element path, defaults to empty array
|
|
508
|
+
* @returns Transaction result
|
|
509
|
+
*/
|
|
510
|
+
async popElement(
|
|
511
|
+
toDomain: string,
|
|
512
|
+
tagName: string,
|
|
513
|
+
elemPath: number[] = []
|
|
514
|
+
): Promise<TransactionResult> {
|
|
515
|
+
// Support Olares ID format
|
|
516
|
+
toDomain = normalizeToDomain(toDomain);
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
const contract = this.console.getSignerContractDID();
|
|
520
|
+
const tx = await contract.popTagElem(
|
|
521
|
+
this.fromDomain,
|
|
522
|
+
toDomain,
|
|
523
|
+
tagName,
|
|
524
|
+
elemPath
|
|
525
|
+
);
|
|
526
|
+
const receipt = await tx.wait();
|
|
527
|
+
|
|
528
|
+
return {
|
|
529
|
+
success: true,
|
|
530
|
+
transactionHash: receipt.hash,
|
|
531
|
+
gasUsed: receipt.gasUsed,
|
|
532
|
+
blockNumber: receipt.blockNumber
|
|
533
|
+
};
|
|
534
|
+
} catch (error: any) {
|
|
535
|
+
const errorInfo = parseContractError(error);
|
|
536
|
+
if (errorInfo.isNetworkError) {
|
|
537
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
538
|
+
}
|
|
539
|
+
throw new Error(`Failed to pop element: ${errorInfo.message}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Update a specific element in an array or tuple
|
|
545
|
+
* @param toDomain Target domain
|
|
546
|
+
* @param tagName Tag name
|
|
547
|
+
* @param elemPath Element path (e.g., [0] for the first element)
|
|
548
|
+
* @param value New value
|
|
549
|
+
* @returns Transaction result
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* // Update the first element of an array
|
|
553
|
+
* await tagCtx.updateElement('example.com', 'links', [0], 'https://new-url.com');
|
|
554
|
+
*
|
|
555
|
+
* // Update an element in a 2D array
|
|
556
|
+
* await tagCtx.updateElement('example.com', 'matrix', [0, 1], 'value');
|
|
557
|
+
*/
|
|
558
|
+
async updateElement(
|
|
559
|
+
toDomain: string,
|
|
560
|
+
tagName: string,
|
|
561
|
+
elemPath: number[],
|
|
562
|
+
value: any
|
|
563
|
+
): Promise<TransactionResult> {
|
|
564
|
+
// Support Olares ID format
|
|
565
|
+
toDomain = normalizeToDomain(toDomain);
|
|
566
|
+
|
|
567
|
+
const contract = this.console.getSignerContractDID();
|
|
568
|
+
|
|
569
|
+
const tagTypeInfo = await this.getTagType(tagName);
|
|
570
|
+
if (!tagTypeInfo) {
|
|
571
|
+
throw new Error(`Tag "${tagName}" is not defined`);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
try {
|
|
575
|
+
// Parse type bytes to ABI string
|
|
576
|
+
const rootAbiType = TagTypeBuilder.parseTypeBytesToAbiString(
|
|
577
|
+
tagTypeInfo.abiType
|
|
578
|
+
);
|
|
579
|
+
// Get element type
|
|
580
|
+
const elementAbiType = this.getElementAbiType(
|
|
581
|
+
rootAbiType,
|
|
582
|
+
elemPath
|
|
583
|
+
);
|
|
584
|
+
// Encode the element value
|
|
585
|
+
const encodedValue = TagAbiCodec.encode(elementAbiType, value);
|
|
586
|
+
|
|
587
|
+
const tx = await contract.updateTagElem(
|
|
588
|
+
this.fromDomain,
|
|
589
|
+
toDomain,
|
|
590
|
+
tagName,
|
|
591
|
+
elemPath,
|
|
592
|
+
encodedValue
|
|
593
|
+
);
|
|
594
|
+
const receipt = await tx.wait();
|
|
595
|
+
|
|
596
|
+
return {
|
|
597
|
+
success: true,
|
|
598
|
+
transactionHash: receipt.hash,
|
|
599
|
+
gasUsed: receipt.gasUsed,
|
|
600
|
+
blockNumber: receipt.blockNumber
|
|
601
|
+
};
|
|
602
|
+
} catch (error: any) {
|
|
603
|
+
const errorInfo = parseContractError(error);
|
|
604
|
+
if (errorInfo.isNetworkError) {
|
|
605
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
606
|
+
}
|
|
607
|
+
throw new Error(`Failed to update element: ${errorInfo.message}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Get the length of an array Tag
|
|
613
|
+
* @param toDomain Target domain
|
|
614
|
+
* @param tagName Tag name
|
|
615
|
+
* @param elemPath Element path, defaults to empty array
|
|
616
|
+
* @returns Array length
|
|
617
|
+
*/
|
|
618
|
+
async getArrayLength(
|
|
619
|
+
toDomain: string,
|
|
620
|
+
tagName: string,
|
|
621
|
+
elemPath: number[] = []
|
|
622
|
+
): Promise<number> {
|
|
623
|
+
// Support Olares ID format
|
|
624
|
+
toDomain = normalizeToDomain(toDomain);
|
|
625
|
+
|
|
626
|
+
const contract = this.console.getContractDID();
|
|
627
|
+
const length = await contract.getTagElemLength(
|
|
628
|
+
this.fromDomain,
|
|
629
|
+
toDomain,
|
|
630
|
+
tagName,
|
|
631
|
+
elemPath
|
|
632
|
+
);
|
|
633
|
+
return Number(length);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Get a specific element from an array or tuple
|
|
638
|
+
* @param toDomain Target domain
|
|
639
|
+
* @param tagName Tag name
|
|
640
|
+
* @param elemPath Element path
|
|
641
|
+
* @returns Element value, or null if not found
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* // Get the first element of an array
|
|
645
|
+
* const first = await tagCtx.getElement('example.com', 'links', [0]);
|
|
646
|
+
*
|
|
647
|
+
* // Get an element from a 2D array
|
|
648
|
+
* const value = await tagCtx.getElement('example.com', 'matrix', [0, 1]);
|
|
649
|
+
*/
|
|
650
|
+
async getElement(
|
|
651
|
+
toDomain: string,
|
|
652
|
+
tagName: string,
|
|
653
|
+
elemPath: number[]
|
|
654
|
+
): Promise<any | null> {
|
|
655
|
+
// Support Olares ID format
|
|
656
|
+
toDomain = normalizeToDomain(toDomain);
|
|
657
|
+
|
|
658
|
+
try {
|
|
659
|
+
const contract = this.console.getContractDID();
|
|
660
|
+
|
|
661
|
+
const encodedValue = await contract.getTagElem(
|
|
662
|
+
this.fromDomain,
|
|
663
|
+
toDomain,
|
|
664
|
+
tagName,
|
|
665
|
+
elemPath
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
// Get tag type and decode element value
|
|
669
|
+
const tagTypeInfo = await this.getTagType(tagName);
|
|
670
|
+
if (!tagTypeInfo) {
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Parse type bytes to ABI string
|
|
675
|
+
const rootAbiType = TagTypeBuilder.parseTypeBytesToAbiString(
|
|
676
|
+
tagTypeInfo.abiType
|
|
677
|
+
);
|
|
678
|
+
// Get element type
|
|
679
|
+
const elementAbiType = this.getElementAbiType(
|
|
680
|
+
rootAbiType,
|
|
681
|
+
elemPath
|
|
682
|
+
);
|
|
683
|
+
// Decode the element
|
|
684
|
+
return TagAbiCodec.decode(elementAbiType, encodedValue);
|
|
685
|
+
} catch (error: any) {
|
|
686
|
+
const errorInfo = parseContractError(error);
|
|
687
|
+
if (errorInfo.errorName === 'TagInvalidOp') {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
if (errorInfo.isNetworkError) {
|
|
691
|
+
throw new Error(`Network error: ${errorInfo.message}`);
|
|
692
|
+
}
|
|
693
|
+
throw error;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// ========================================
|
|
698
|
+
// Helper Methods (Private)
|
|
699
|
+
// ========================================
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Get element ABI type based on path
|
|
703
|
+
* Example: string[][] + [0] = string[]
|
|
704
|
+
* string[][] + [0, 0] = string
|
|
705
|
+
*
|
|
706
|
+
* Special case: For pushElement/popElement on array type with empty path,
|
|
707
|
+
* we want the element type:
|
|
708
|
+
* Example: string[] + [] → string (for push operation)
|
|
709
|
+
*/
|
|
710
|
+
private getElementAbiType(abiType: string, elemPath: number[]): string {
|
|
711
|
+
let currentType = abiType;
|
|
712
|
+
|
|
713
|
+
// If elemPath is empty and currentType is an array,
|
|
714
|
+
// return the element type (for push/pop operations)
|
|
715
|
+
if (elemPath.length === 0) {
|
|
716
|
+
const arrayMatch = currentType.match(/^(.+?)\[(\d*)\]$/);
|
|
717
|
+
if (arrayMatch) {
|
|
718
|
+
return arrayMatch[1]; // Return element type
|
|
719
|
+
}
|
|
720
|
+
// Not an array, return as-is
|
|
721
|
+
return currentType;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Process each level of the path
|
|
725
|
+
for (const _index of elemPath) {
|
|
726
|
+
// Parse ABI type string
|
|
727
|
+
// Handle array types: type[] or type[N]
|
|
728
|
+
const arrayMatch = currentType.match(/^(.+?)\[(\d*)\]$/);
|
|
729
|
+
|
|
730
|
+
if (arrayMatch) {
|
|
731
|
+
// Array type, element type is the base type
|
|
732
|
+
currentType = arrayMatch[1];
|
|
733
|
+
} else if (currentType.startsWith('tuple(')) {
|
|
734
|
+
// Tuple type - not supported for element access by index
|
|
735
|
+
throw new Error(
|
|
736
|
+
'Tuple element access by index not supported yet. Use field names instead.'
|
|
737
|
+
);
|
|
738
|
+
} else {
|
|
739
|
+
throw new Error(
|
|
740
|
+
`Cannot access element of non-array type "${currentType}" at path ${elemPath}`
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return currentType;
|
|
746
|
+
}
|
|
747
|
+
}
|