@beclab/olaresid 0.1.13 → 0.2.1
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-TREE.md +107 -0
- package/CLI.md +122 -1340
- package/README.md +30 -12
- package/SDK-TREE.md +151 -0
- package/TAG.md +95 -41
- package/config.json +6 -4
- package/dist/abi/TerminusDIDQueryABI.d.ts +397 -0
- package/dist/abi/TerminusDIDQueryABI.d.ts.map +1 -0
- package/dist/abi/TerminusDIDQueryABI.js +519 -0
- package/dist/abi/TerminusDIDQueryABI.js.map +1 -0
- package/dist/business/index.d.ts.map +1 -1
- package/dist/business/index.js +9 -23
- package/dist/business/index.js.map +1 -1
- package/dist/business/tag-context.d.ts +1 -0
- package/dist/business/tag-context.d.ts.map +1 -1
- package/dist/business/tag-context.js +13 -7
- package/dist/business/tag-context.js.map +1 -1
- package/dist/cli.js +177 -76
- package/dist/cli.js.map +1 -1
- package/dist/config/index.d.ts +16 -4
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +28 -14
- package/dist/config/index.js.map +1 -1
- package/dist/domain/core.d.ts +65 -0
- package/dist/domain/core.d.ts.map +1 -0
- package/dist/domain/core.js +317 -0
- package/dist/domain/core.js.map +1 -0
- package/dist/domain/index.d.ts +104 -57
- package/dist/domain/index.d.ts.map +1 -1
- package/dist/domain/index.js +188 -428
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/types.d.ts +56 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +3 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/index.d.ts +81 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +158 -143
- package/dist/index.js.map +1 -1
- package/dist/utils/crypto-utils.d.ts +110 -0
- package/dist/utils/crypto-utils.d.ts.map +1 -1
- package/dist/utils/crypto-utils.js +127 -8
- package/dist/utils/crypto-utils.js.map +1 -1
- package/dist/utils/error-parser.d.ts.map +1 -1
- package/dist/utils/error-parser.js +2 -1
- package/dist/utils/error-parser.js.map +1 -1
- package/dist/utils/event-parser.d.ts +161 -0
- package/dist/utils/event-parser.d.ts.map +1 -0
- package/dist/utils/event-parser.js +140 -0
- package/dist/utils/event-parser.js.map +1 -0
- package/dist/utils/tag-type-builder.d.ts +43 -0
- package/dist/utils/tag-type-builder.d.ts.map +1 -1
- package/dist/utils/tag-type-builder.js +122 -0
- package/dist/utils/tag-type-builder.js.map +1 -1
- package/dist/utils/tag-type-parser.d.ts +70 -0
- package/dist/utils/tag-type-parser.d.ts.map +1 -0
- package/dist/utils/tag-type-parser.js +190 -0
- package/dist/utils/tag-type-parser.js.map +1 -0
- package/examples/create-with-rpc-demo.ts +142 -0
- package/examples/fetch-all-flat-demo.ts +159 -0
- package/examples/fetch-by-indices-demo.ts +235 -0
- package/examples/fetch-domain-demo.ts +137 -0
- package/examples/fetch-domains-demo.ts +221 -0
- package/examples/frontend-demo/index.html +2 -2
- package/examples/frontend-demo/package-lock.json +4 -1
- package/examples/index.ts +3 -5
- package/jest.config.js +25 -0
- package/package.json +6 -2
- package/src/abi/TerminusDIDQueryABI.ts +516 -0
- package/src/business/index.ts +9 -33
- package/src/business/tag-context.ts +35 -7
- package/src/cli.ts +253 -90
- package/src/config/index.ts +34 -19
- package/src/domain/core.ts +382 -0
- package/src/domain/index.ts +271 -641
- package/src/domain/types.ts +59 -0
- package/src/index.ts +230 -207
- package/src/utils/crypto-utils.ts +205 -2
- package/src/utils/error-parser.ts +2 -1
- package/src/utils/event-parser.ts +353 -0
- package/src/utils/tag-type-builder.ts +138 -0
- package/src/utils/tag-type-parser.ts +246 -0
- package/tests/unit/crypto-utils.test.ts +338 -0
- package/tests/unit/ed25519-jwk.test.ts +201 -0
- package/tests/unit/event-parser.test.ts +690 -0
- package/tests/unit/generate-mnemonic.test.ts +268 -0
- package/tests/unit/olares-id-format.test.ts +321 -0
- package/tests/unit/tag-type-parser.test.ts +802 -0
- package/tests/unit/tag-types.test.ts +821 -0
- package/tests/unit/version.test.ts +14 -0
- package/tsconfig.json +3 -2
- package/dist/abi/ABITypeABI.d.ts +0 -88
- package/dist/abi/ABITypeABI.d.ts.map +0 -1
- package/dist/abi/ABITypeABI.js +0 -382
- package/dist/abi/ABITypeABI.js.map +0 -1
- package/dist/abi/RegistryABI.d.ts +0 -77
- package/dist/abi/RegistryABI.d.ts.map +0 -1
- package/dist/abi/RegistryABI.js +0 -462
- package/dist/abi/RegistryABI.js.map +0 -1
- package/dist/tag/address.d.ts +0 -11
- package/dist/tag/address.d.ts.map +0 -1
- package/dist/tag/address.js +0 -44
- package/dist/tag/address.js.map +0 -1
- package/dist/tag/array.d.ts +0 -14
- package/dist/tag/array.d.ts.map +0 -1
- package/dist/tag/array.js +0 -72
- package/dist/tag/array.js.map +0 -1
- package/dist/tag/bool.d.ts +0 -11
- package/dist/tag/bool.d.ts.map +0 -1
- package/dist/tag/bool.js +0 -43
- package/dist/tag/bool.js.map +0 -1
- package/dist/tag/bytes.d.ts +0 -11
- package/dist/tag/bytes.d.ts.map +0 -1
- package/dist/tag/bytes.js +0 -37
- package/dist/tag/bytes.js.map +0 -1
- package/dist/tag/flarray.d.ts +0 -15
- package/dist/tag/flarray.d.ts.map +0 -1
- package/dist/tag/flarray.js +0 -81
- package/dist/tag/flarray.js.map +0 -1
- package/dist/tag/flbytes.d.ts +0 -11
- package/dist/tag/flbytes.d.ts.map +0 -1
- package/dist/tag/flbytes.js +0 -47
- package/dist/tag/flbytes.js.map +0 -1
- package/dist/tag/index.d.ts +0 -32
- package/dist/tag/index.d.ts.map +0 -1
- package/dist/tag/index.js +0 -121
- package/dist/tag/index.js.map +0 -1
- package/dist/tag/int.d.ts +0 -12
- package/dist/tag/int.d.ts.map +0 -1
- package/dist/tag/int.js +0 -49
- package/dist/tag/int.js.map +0 -1
- package/dist/tag/string.d.ts +0 -11
- package/dist/tag/string.d.ts.map +0 -1
- package/dist/tag/string.js +0 -37
- package/dist/tag/string.js.map +0 -1
- package/dist/tag/tag.d.ts +0 -67
- package/dist/tag/tag.d.ts.map +0 -1
- package/dist/tag/tag.js +0 -157
- package/dist/tag/tag.js.map +0 -1
- package/dist/tag/tuple.d.ts +0 -17
- package/dist/tag/tuple.d.ts.map +0 -1
- package/dist/tag/tuple.js +0 -162
- package/dist/tag/tuple.js.map +0 -1
- package/dist/tag/uint.d.ts +0 -12
- package/dist/tag/uint.d.ts.map +0 -1
- package/dist/tag/uint.js +0 -49
- package/dist/tag/uint.js.map +0 -1
- package/dist/test/did.d.ts +0 -2
- package/dist/test/did.d.ts.map +0 -1
- package/dist/test/did.js +0 -177
- package/dist/test/did.js.map +0 -1
- package/dist/utils/tag-abi-codec.d.ts +0 -69
- package/dist/utils/tag-abi-codec.d.ts.map +0 -1
- package/dist/utils/tag-abi-codec.js +0 -144
- package/dist/utils/tag-abi-codec.js.map +0 -1
- package/examples/crypto-utilities.ts +0 -140
- package/examples/ed25519-jwk.ts +0 -73
- package/examples/generate-mnemonic.ts +0 -149
- package/examples/legacy.ts +0 -33
- package/examples/olares-id-format.ts +0 -197
- package/examples/tag-builder.ts +0 -235
- package/examples/tag-nested-tuple.ts +0 -190
- package/examples/tag-simple.ts +0 -149
- package/examples/tag-tagger.ts +0 -217
- package/examples/test-nested-tuple-conversion.ts +0 -143
- package/examples/test-type-bytes-parser.ts +0 -70
- package/src/abi/ABITypeABI.ts +0 -379
- package/src/abi/RegistryABI.ts +0 -459
- package/src/tag/address.ts +0 -48
- package/src/tag/array.ts +0 -80
- package/src/tag/bool.ts +0 -43
- package/src/tag/bytes.ts +0 -38
- package/src/tag/flarray.ts +0 -99
- package/src/tag/flbytes.ts +0 -48
- package/src/tag/index.ts +0 -170
- package/src/tag/int.ts +0 -51
- package/src/tag/string.ts +0 -38
- package/src/tag/tag.ts +0 -229
- package/src/tag/tuple.ts +0 -193
- package/src/tag/uint.ts +0 -51
- package/src/test/did.ts +0 -346
- package/src/utils/tag-abi-codec.ts +0 -158
package/src/config/index.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import configData from '../../config.json';
|
|
3
4
|
|
|
4
5
|
export interface NetworkConfig {
|
|
5
6
|
rpc: string;
|
|
6
7
|
contractDid: string;
|
|
7
8
|
contractRootResolver: string;
|
|
8
9
|
contractAbiType: string;
|
|
9
|
-
contractRootResolver2
|
|
10
|
-
supportSvcUrl
|
|
10
|
+
contractRootResolver2?: string;
|
|
11
|
+
supportSvcUrl?: string;
|
|
12
|
+
queryContract?: string;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export interface ConfigData {
|
|
@@ -16,34 +18,31 @@ export interface ConfigData {
|
|
|
16
18
|
};
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
export interface ContractConfig {
|
|
22
|
+
contractDid: string;
|
|
23
|
+
contractRootResolver: string;
|
|
24
|
+
contractAbiType: string;
|
|
25
|
+
contractRootResolver2?: string;
|
|
26
|
+
}
|
|
21
27
|
|
|
22
28
|
/**
|
|
23
|
-
* Load configuration from
|
|
29
|
+
* Load configuration from imported JSON
|
|
30
|
+
* Works in both Node.js and browser environments
|
|
24
31
|
*/
|
|
25
32
|
export function loadConfig(): ConfigData {
|
|
26
|
-
|
|
27
|
-
const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
28
|
-
return JSON.parse(data);
|
|
29
|
-
} catch (error) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`Failed to load config from ${CONFIG_FILE}: ${
|
|
32
|
-
error instanceof Error ? error.message : String(error)
|
|
33
|
-
}`
|
|
34
|
-
);
|
|
35
|
-
}
|
|
33
|
+
return configData;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
/**
|
|
39
|
-
* Save configuration to file
|
|
37
|
+
* Save configuration to file (Node.js only)
|
|
40
38
|
*/
|
|
41
39
|
export function saveConfig(config: ConfigData): void {
|
|
42
40
|
try {
|
|
43
|
-
|
|
41
|
+
const configPath = path.join(__dirname, '../../config.json');
|
|
42
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 4));
|
|
44
43
|
} catch (error) {
|
|
45
44
|
throw new Error(
|
|
46
|
-
`Failed to save config
|
|
45
|
+
`Failed to save config: ${
|
|
47
46
|
error instanceof Error ? error.message : String(error)
|
|
48
47
|
}`
|
|
49
48
|
);
|
|
@@ -70,5 +69,21 @@ export function getAvailableNetworks(): string[] {
|
|
|
70
69
|
* Get configuration file path
|
|
71
70
|
*/
|
|
72
71
|
export function getConfigFilePath(): string {
|
|
73
|
-
return
|
|
72
|
+
return path.join(__dirname, '../../config.json');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get contract configuration for a specific network
|
|
77
|
+
*/
|
|
78
|
+
export function getContractConfig(network: string): ContractConfig | undefined {
|
|
79
|
+
const networkConfig = getNetworkConfig(network);
|
|
80
|
+
if (!networkConfig) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
contractDid: networkConfig.contractDid,
|
|
85
|
+
contractRootResolver: networkConfig.contractRootResolver,
|
|
86
|
+
contractAbiType: networkConfig.contractAbiType,
|
|
87
|
+
contractRootResolver2: networkConfig.contractRootResolver2
|
|
88
|
+
};
|
|
74
89
|
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { ethers, AbiCoder } from 'ethers';
|
|
2
|
+
import QUERY_CONTRACT_ABI from '../abi/TerminusDIDQueryABI';
|
|
3
|
+
import { RawDomainData, RawTagData, FlatDomainData, Tag } from './types';
|
|
4
|
+
import { TagTypeParser } from '../utils/tag-type-parser';
|
|
5
|
+
import { parseContractError } from '../utils/error-parser';
|
|
6
|
+
import { getFullyQualifiedVerificationMethodID } from '../utils/crypto-utils';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Query Contract Helper
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Helper class for interacting with TerminusDIDQuery contract
|
|
14
|
+
*/
|
|
15
|
+
export class QueryContractHelper {
|
|
16
|
+
private contract: ethers.Contract;
|
|
17
|
+
|
|
18
|
+
constructor(queryContractAddress: string, provider: ethers.Provider) {
|
|
19
|
+
this.contract = new ethers.Contract(
|
|
20
|
+
queryContractAddress,
|
|
21
|
+
QUERY_CONTRACT_ABI,
|
|
22
|
+
provider
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get total supply of domains
|
|
28
|
+
*/
|
|
29
|
+
async getTotalSupply(): Promise<number> {
|
|
30
|
+
try {
|
|
31
|
+
const supply = await this.contract.totalSupply();
|
|
32
|
+
return Number(supply);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
const parsedError = parseContractError(error);
|
|
35
|
+
console.error('[core] getTotalSupply failed:', parsedError.message);
|
|
36
|
+
throw new Error(parsedError.message);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get token ID by index
|
|
42
|
+
*/
|
|
43
|
+
async getTokenByIndex(index: number): Promise<bigint> {
|
|
44
|
+
try {
|
|
45
|
+
return await this.contract.tokenByIndex(index);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const parsedError = parseContractError(error);
|
|
48
|
+
console.error(
|
|
49
|
+
'[core] getTokenByIndex failed:',
|
|
50
|
+
parsedError.message
|
|
51
|
+
);
|
|
52
|
+
throw new Error(parsedError.message);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get single domain data by token ID or domain name
|
|
58
|
+
* Automatically detects if input is tokenId (starts with 0x or is bigint) or domain name
|
|
59
|
+
*/
|
|
60
|
+
async getDomain(
|
|
61
|
+
nameOrTokenId: string | bigint
|
|
62
|
+
): Promise<RawDomainData | null> {
|
|
63
|
+
try {
|
|
64
|
+
let data: any;
|
|
65
|
+
|
|
66
|
+
// Check if it's a tokenId (hex string or bigint)
|
|
67
|
+
if (
|
|
68
|
+
typeof nameOrTokenId === 'bigint' ||
|
|
69
|
+
(typeof nameOrTokenId === 'string' &&
|
|
70
|
+
nameOrTokenId.startsWith('0x'))
|
|
71
|
+
) {
|
|
72
|
+
data = await this.contract.getDomain(nameOrTokenId);
|
|
73
|
+
} else {
|
|
74
|
+
// It's a domain name
|
|
75
|
+
data = await this.contract.getDomainByName(nameOrTokenId);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return this._convertDomainData(data);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const parsedError = parseContractError(error);
|
|
81
|
+
console.error('[core] getDomain failed:', parsedError.message);
|
|
82
|
+
throw new Error(parsedError.message);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get multiple domains data by token IDs
|
|
88
|
+
*/
|
|
89
|
+
async getDomains(tokenIds: (string | bigint)[]): Promise<RawDomainData[]> {
|
|
90
|
+
try {
|
|
91
|
+
const data = await this.contract.getDomains(tokenIds);
|
|
92
|
+
return data.map((d: any) => this._convertDomainData(d));
|
|
93
|
+
} catch (error) {
|
|
94
|
+
const parsedError = parseContractError(error);
|
|
95
|
+
console.error('[core] getDomains failed:', parsedError.message);
|
|
96
|
+
throw new Error(parsedError.message);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get all domains with pagination
|
|
102
|
+
*/
|
|
103
|
+
async getAllDomains(
|
|
104
|
+
offset: number,
|
|
105
|
+
limit: number
|
|
106
|
+
): Promise<RawDomainData[]> {
|
|
107
|
+
try {
|
|
108
|
+
const data = await this.contract.getAllDomains(offset, limit);
|
|
109
|
+
return data.map((d: any) => this._convertDomainData(d));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const parsedError = parseContractError(error);
|
|
112
|
+
console.error('[core] getAllDomains failed:', parsedError.message);
|
|
113
|
+
throw new Error(parsedError.message);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get domains by specific indices (single RPC call)
|
|
119
|
+
*/
|
|
120
|
+
async getDomainsByIndices(indices: number[]): Promise<RawDomainData[]> {
|
|
121
|
+
try {
|
|
122
|
+
const data = await this.contract.getDomainsByIndices(indices);
|
|
123
|
+
return data.map((d: any) => this._convertDomainData(d));
|
|
124
|
+
} catch (error) {
|
|
125
|
+
const parsedError = parseContractError(error);
|
|
126
|
+
console.error(
|
|
127
|
+
'[core] getDomainsByIndices failed:',
|
|
128
|
+
parsedError.message
|
|
129
|
+
);
|
|
130
|
+
throw new Error(parsedError.message);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Convert contract response to RawDomainData
|
|
136
|
+
*/
|
|
137
|
+
private _convertDomainData(data: any): RawDomainData {
|
|
138
|
+
return {
|
|
139
|
+
tokenId: data.tokenId,
|
|
140
|
+
domain: data.domain,
|
|
141
|
+
did: data.did,
|
|
142
|
+
notes: data.notes,
|
|
143
|
+
allowSubdomain: data.allowSubdomain,
|
|
144
|
+
owner: data.owner,
|
|
145
|
+
tags: data.tags.map((tag: any) => ({
|
|
146
|
+
from: tag.from,
|
|
147
|
+
name: tag.name,
|
|
148
|
+
// ethers v6 returns bytes fields as Uint8Array; normalize to hex string
|
|
149
|
+
abiType: ethers.hexlify(tag.abiType ?? '0x'),
|
|
150
|
+
value: ethers.hexlify(tag.value ?? '0x'),
|
|
151
|
+
fieldNamesHash: tag.fieldNamesHash
|
|
152
|
+
}))
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Field Names Fetching
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Collect all unique field name hashes from raw domains
|
|
163
|
+
*/
|
|
164
|
+
export function collectFieldNameHashes(rawDomains: RawDomainData[]): string[] {
|
|
165
|
+
const hashSet = new Set<string>();
|
|
166
|
+
|
|
167
|
+
for (const domain of rawDomains) {
|
|
168
|
+
for (const tag of domain.tags) {
|
|
169
|
+
for (const hash of tag.fieldNamesHash) {
|
|
170
|
+
if (
|
|
171
|
+
hash &&
|
|
172
|
+
hash !==
|
|
173
|
+
'0x0000000000000000000000000000000000000000000000000000000000000000'
|
|
174
|
+
) {
|
|
175
|
+
hashSet.add(hash);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return Array.from(hashSet);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Batch fetch field names from blockchain events
|
|
186
|
+
* Groups queries by block number to minimize RPC calls
|
|
187
|
+
*/
|
|
188
|
+
export async function batchFetchFieldNames(
|
|
189
|
+
contract: ethers.Contract,
|
|
190
|
+
hashes: string[]
|
|
191
|
+
): Promise<Map<string, string[]>> {
|
|
192
|
+
const fieldNamesMap = new Map<string, string[]>();
|
|
193
|
+
|
|
194
|
+
if (hashes.length === 0) {
|
|
195
|
+
return fieldNamesMap;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Step 1: Batch get block numbers for all hashes
|
|
199
|
+
const hashToBlock = new Map<string, number>();
|
|
200
|
+
for (const hash of hashes) {
|
|
201
|
+
try {
|
|
202
|
+
const blockNum = await contract.getFieldNamesEventBlock(hash);
|
|
203
|
+
hashToBlock.set(hash, Number(blockNum));
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error(
|
|
206
|
+
`[core] failed to get block number for field name hash ${hash}:`,
|
|
207
|
+
error instanceof Error ? error.message : String(error)
|
|
208
|
+
);
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Step 2: Group hashes by block number
|
|
214
|
+
const blockToHashes = new Map<number, string[]>();
|
|
215
|
+
for (const [hash, blockNum] of hashToBlock.entries()) {
|
|
216
|
+
if (!blockToHashes.has(blockNum)) {
|
|
217
|
+
blockToHashes.set(blockNum, []);
|
|
218
|
+
}
|
|
219
|
+
blockToHashes.get(blockNum)!.push(hash);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Step 3: Batch query events by block
|
|
223
|
+
for (const [blockNum, hashesInBlock] of blockToHashes.entries()) {
|
|
224
|
+
try {
|
|
225
|
+
const events = await contract.queryFilter(
|
|
226
|
+
'OffchainStringArray',
|
|
227
|
+
blockNum,
|
|
228
|
+
blockNum
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
for (const event of events) {
|
|
232
|
+
const eventHash = (event as any).args.hash;
|
|
233
|
+
if (hashesInBlock.includes(eventHash)) {
|
|
234
|
+
fieldNamesMap.set(eventHash, (event as any).args.value);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error(
|
|
239
|
+
`[core] failed to query events at block ${blockNum} for ${hashesInBlock.length} hashes:`,
|
|
240
|
+
error instanceof Error ? error.message : String(error)
|
|
241
|
+
);
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return fieldNamesMap;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// Domain Parsing
|
|
251
|
+
// ============================================================================
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Parse a single raw domain into flat domain data
|
|
255
|
+
*/
|
|
256
|
+
export function parseDomain(
|
|
257
|
+
rawDomain: RawDomainData,
|
|
258
|
+
fieldNamesMap: Map<string, string[]>
|
|
259
|
+
): FlatDomainData {
|
|
260
|
+
const abiCoder = new AbiCoder();
|
|
261
|
+
|
|
262
|
+
const domain: FlatDomainData = {
|
|
263
|
+
id: ethers.toBeHex(rawDomain.tokenId, 32),
|
|
264
|
+
name: rawDomain.domain,
|
|
265
|
+
did: rawDomain.did,
|
|
266
|
+
note: rawDomain.notes,
|
|
267
|
+
allowSubdomain: rawDomain.allowSubdomain,
|
|
268
|
+
owner: ethers.getAddress(rawDomain.owner),
|
|
269
|
+
level: calculateLevel(rawDomain.domain),
|
|
270
|
+
tags: []
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Parse tags
|
|
274
|
+
for (const rawTag of rawDomain.tags) {
|
|
275
|
+
try {
|
|
276
|
+
const tag = TagTypeParser.parseTag(rawTag, fieldNamesMap, abiCoder);
|
|
277
|
+
domain.tags.push(tag);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.error(`[core] failed to parse tag ${rawTag.name}:`, error);
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
applyLatestDID(domain);
|
|
285
|
+
|
|
286
|
+
return domain;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Parse all raw domains into flat domain data
|
|
291
|
+
* Also calculates parentId for each domain
|
|
292
|
+
*/
|
|
293
|
+
export function parseAllDomains(
|
|
294
|
+
rawDomains: RawDomainData[],
|
|
295
|
+
fieldNamesMap: Map<string, string[]>
|
|
296
|
+
): FlatDomainData[] {
|
|
297
|
+
const abiCoder = new AbiCoder();
|
|
298
|
+
|
|
299
|
+
// Step 1: Create all domain objects
|
|
300
|
+
const domainMap = new Map<string, FlatDomainData>();
|
|
301
|
+
|
|
302
|
+
for (const rawDomain of rawDomains) {
|
|
303
|
+
const domain: FlatDomainData = {
|
|
304
|
+
id: ethers.toBeHex(rawDomain.tokenId, 32),
|
|
305
|
+
name: rawDomain.domain,
|
|
306
|
+
did: rawDomain.did,
|
|
307
|
+
note: rawDomain.notes,
|
|
308
|
+
allowSubdomain: rawDomain.allowSubdomain,
|
|
309
|
+
owner: ethers.getAddress(rawDomain.owner),
|
|
310
|
+
level: calculateLevel(rawDomain.domain),
|
|
311
|
+
tags: []
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// Parse tags
|
|
315
|
+
for (const rawTag of rawDomain.tags) {
|
|
316
|
+
try {
|
|
317
|
+
const tag = TagTypeParser.parseTag(
|
|
318
|
+
rawTag,
|
|
319
|
+
fieldNamesMap,
|
|
320
|
+
abiCoder
|
|
321
|
+
);
|
|
322
|
+
domain.tags.push(tag);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.error(
|
|
325
|
+
`[core] failed to parse tag ${rawTag.name}:`,
|
|
326
|
+
error
|
|
327
|
+
);
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
applyLatestDID(domain);
|
|
333
|
+
|
|
334
|
+
domainMap.set(domain.name, domain);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Step 2: Calculate parentId
|
|
338
|
+
const flatDomains: FlatDomainData[] = [];
|
|
339
|
+
for (const domain of domainMap.values()) {
|
|
340
|
+
if (domain.level > 1) {
|
|
341
|
+
const parentName = getParentDomainName(domain.name);
|
|
342
|
+
const parent = domainMap.get(parentName);
|
|
343
|
+
if (parent) {
|
|
344
|
+
domain.parentId = parent.id;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
flatDomains.push(domain);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return flatDomains;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ============================================================================
|
|
354
|
+
// Utility Functions
|
|
355
|
+
// ============================================================================
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* If a domain has a latestDID tag with a non-empty valueFormatted, override did with it
|
|
359
|
+
*/
|
|
360
|
+
function applyLatestDID(domain: FlatDomainData): void {
|
|
361
|
+
const latestDIDTag = domain.tags.find((t) => t.name === 'latestDID');
|
|
362
|
+
if (latestDIDTag?.valueFormatted) {
|
|
363
|
+
domain.did = getFullyQualifiedVerificationMethodID(
|
|
364
|
+
latestDIDTag.valueFormatted
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Calculate domain level (number of dots + 1)
|
|
371
|
+
*/
|
|
372
|
+
export function calculateLevel(domainName: string): number {
|
|
373
|
+
return domainName.split('.').length;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Get parent domain name
|
|
378
|
+
*/
|
|
379
|
+
export function getParentDomainName(domainName: string): string {
|
|
380
|
+
const parts = domainName.split('.');
|
|
381
|
+
return parts.slice(1).join('.');
|
|
382
|
+
}
|