@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.
Files changed (182) 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 +81 -23
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +158 -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 +230 -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/tests/unit/version.test.ts +14 -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
@@ -82,7 +82,7 @@ async function loadWalletCore(): Promise<any> {
82
82
  }
83
83
 
84
84
  // multicodec code for Ed25519 keys (0xed)
85
- const ED25519_CODEC_ID = varint.encode(parseInt('0xed', 16));
85
+ export const ED25519_CODEC_ID = varint.encode(parseInt('0xed', 16));
86
86
 
87
87
  // ============================================================================
88
88
  // Mnemonic and Key Derivation Functions
@@ -334,6 +334,7 @@ export async function getEd25519JwkFromMnemonic(mnemonic: string): Promise<{
334
334
  idBytes.set(publicKeyBytes, ED25519_CODEC_ID.length);
335
335
  const id = base58btc.encode(idBytes);
336
336
  const did = `did:key:${id}`;
337
+ const kid = `${did}#${id}`; // Fully-qualified verification method ID
337
338
 
338
339
  // Base64url encode the keys
339
340
  const x = base64url.baseEncode(publicKeyBytes);
@@ -345,7 +346,7 @@ export async function getEd25519JwkFromMnemonic(mnemonic: string): Promise<{
345
346
  crv: 'Ed25519', // Curve: Ed25519
346
347
  alg: 'EdDSA', // Algorithm: EdDSA
347
348
  use: 'sig', // Use: signature
348
- kid: did, // DID
349
+ kid: kid, // Fully-qualified verification method ID (did:key:z...#z...)
349
350
  x: x // Public key parameter
350
351
  };
351
352
 
@@ -547,3 +548,205 @@ export function createRsaKeyPair(
547
548
  return generateKeyPairBrowser(length);
548
549
  }
549
550
  }
551
+
552
+ // ============================================================================
553
+ // DID Type Definitions (from @bytetrade/core)
554
+ // ============================================================================
555
+
556
+ /**
557
+ * Jwk base type - identifies the cryptographic algorithm family used with the key.
558
+ */
559
+ interface Jwk {
560
+ /** The "alg" (algorithm) parameter identifies the algorithm intended for use with the key. */
561
+ alg?: string;
562
+ /** The "kid" (key ID) parameter identifies the key. */
563
+ kid?: string;
564
+ /** identifies the cryptographic algorithm family used with the key, such "EC" or "OKP". */
565
+ kty: string;
566
+ /** The "use" (public key use) parameter identifies the intended use of the public key. */
567
+ use?: string;
568
+ }
569
+
570
+ /**
571
+ * PublicJwk represents a public key in JWK format.
572
+ */
573
+ export interface PublicJwk extends Jwk {
574
+ /** The "crv" (curve) parameter identifies the cryptographic curve used with the key. */
575
+ crv: string;
576
+ /** The x coordinate for the Elliptic Curve point, base64url encoded. */
577
+ x: string;
578
+ /** The y coordinate for the Elliptic Curve point, base64url encoded. Optional for Ed25519. */
579
+ y?: string;
580
+ }
581
+
582
+ /**
583
+ * PrivateJwk represents a private key in JWK format.
584
+ */
585
+ export interface PrivateJwk extends PublicJwk {
586
+ /** The private key value, base64url encoded. */
587
+ d: string;
588
+ }
589
+
590
+ /**
591
+ * Linked Data Key Types - identifies the type of verification method.
592
+ */
593
+ export enum LDKeyType {
594
+ X25519KeyAgreementKey2020 = 'X25519KeyAgreementKey2020',
595
+ Ed25519VerificationKey2020 = 'Ed25519VerificationKey2020',
596
+ X25519KeyAgreementKey2019 = 'X25519KeyAgreementKey2019',
597
+ Ed25519VerificationKey2018 = 'Ed25519VerificationKey2018',
598
+ EcdsaSecp256k1VerificationKey2019 = 'EcdsaSecp256k1VerificationKey2019'
599
+ }
600
+
601
+ /**
602
+ * VerificationMethod represents a verification method in a DID document.
603
+ */
604
+ export interface VerificationMethod {
605
+ id: string;
606
+ type: LDKeyType | string;
607
+ controller: string;
608
+ publicKeyBase58?: string;
609
+ publicKeyJwk?: PublicJwk;
610
+ publicKeyMultibase?: string;
611
+ blockchainAccountId?: string;
612
+ }
613
+
614
+ /**
615
+ * DIDDocument represents a W3C DID Document.
616
+ */
617
+ export interface DIDDocument {
618
+ '@context'?: any;
619
+ id?: string;
620
+ controller?: string;
621
+ alsoKnownAs?: string;
622
+ verificationMethod?: VerificationMethod[];
623
+ authentication?: (string | VerificationMethod)[];
624
+ assertionMethod?: (string | VerificationMethod)[];
625
+ keyAgreement?: (string | VerificationMethod)[];
626
+ capabilityInvocation?: (string | VerificationMethod)[];
627
+ capabilityDelegation?: (string | VerificationMethod)[];
628
+ services?: Service[];
629
+ }
630
+
631
+ /**
632
+ * Service endpoint in a DID document.
633
+ */
634
+ export interface Service {
635
+ id: string;
636
+ type: string;
637
+ serviceEndpoint: any;
638
+ routingKeys?: string[];
639
+ accept?: string[];
640
+ }
641
+
642
+ // ============================================================================
643
+ // DID / JWK Utilities
644
+ // ============================================================================
645
+
646
+ /**
647
+ * Build a fully-qualified verification method ID from a DID.
648
+ * e.g. "did:key:z6Mk..." → "did:key:z6Mk...#z6Mk..."
649
+ */
650
+ export function getFullyQualifiedVerificationMethodID(did: string): string {
651
+ const base = did.split('#')[0];
652
+ const [, , id] = base.split(':');
653
+ return `${base}#${id}`;
654
+ }
655
+
656
+ /**
657
+ * Construct a PublicJwk from a DID and an already-decoded base64url `x` value.
658
+ */
659
+ export function getPublicJWKFromX(did: string, x: string): PublicJwk {
660
+ return {
661
+ alg: 'EdDSA',
662
+ crv: 'Ed25519',
663
+ kid: did,
664
+ kty: 'OKP',
665
+ use: 'sig',
666
+ x
667
+ };
668
+ }
669
+
670
+ /**
671
+ * Derive a PublicJwk directly from a did:key string.
672
+ */
673
+ export function getPublicJWKFromDID(did: string): PublicJwk {
674
+ const base = did.split('#')[0];
675
+ const [scheme, method, id] = base.split(':');
676
+ if (scheme !== 'did')
677
+ throw new Error('Malformed DID: scheme must be "did"');
678
+ if (method !== 'key')
679
+ throw new Error('Unsupported DID method: must be "key"');
680
+
681
+ const idBytes = base58btc.decode(id);
682
+ const x = base64url.baseEncode(idBytes.slice(ED25519_CODEC_ID.length));
683
+ return getPublicJWKFromX(base, x);
684
+ }
685
+
686
+ /**
687
+ * Construct a PrivateJwk from a DID and a base64url-encoded private key `d`.
688
+ */
689
+ export function getPrivateJWKFromD(did: string, d: string): PrivateJwk {
690
+ return { ...getPublicJWKFromDID(did), d };
691
+ }
692
+
693
+ /**
694
+ * Encode a raw Ed25519 public key (Uint8Array) to a multicodec base58btc id string.
695
+ */
696
+ export function getIDFromEd25519Pubkey(publicKey: Uint8Array): string {
697
+ const idBytes = new Uint8Array(publicKey.length + ED25519_CODEC_ID.length);
698
+ idBytes.set(ED25519_CODEC_ID, 0);
699
+ idBytes.set(publicKey, ED25519_CODEC_ID.length);
700
+ return base58btc.encode(idBytes);
701
+ }
702
+
703
+ /**
704
+ * Construct a PrivateJwk from raw Ed25519 public/private key bytes.
705
+ */
706
+ export function getPrivateJWKFromEd25519(
707
+ publicKey: Uint8Array,
708
+ privateKey: Uint8Array
709
+ ): PrivateJwk {
710
+ const id = getIDFromEd25519Pubkey(publicKey);
711
+ const keyId = `did:key:${id}#${id}`;
712
+ return {
713
+ alg: 'EdDSA',
714
+ crv: 'Ed25519',
715
+ kid: keyId,
716
+ kty: 'OKP',
717
+ use: 'sig',
718
+ x: base64url.baseEncode(publicKey),
719
+ d: base64url.baseEncode(privateKey)
720
+ };
721
+ }
722
+
723
+ /**
724
+ * Resolve a did:key DID into a W3C DIDDocument.
725
+ */
726
+ export function resolveDID(did: string): DIDDocument {
727
+ const publicJwk = getPublicJWKFromDID(did);
728
+ const base = did.split('#')[0];
729
+ const [, , id] = base.split(':');
730
+ const methodId = `#${id}`;
731
+
732
+ return {
733
+ '@context': [
734
+ 'https://www.w3.org/ns/did/v1',
735
+ 'https://w3id.org/security/suites/ed25519-2020/v1',
736
+ 'https://w3id.org/security/suites/x25519-2020/v1'
737
+ ],
738
+ id: base,
739
+ verificationMethod: [
740
+ {
741
+ id: methodId,
742
+ type: LDKeyType.Ed25519VerificationKey2020,
743
+ controller: base,
744
+ publicKeyJwk: publicJwk
745
+ }
746
+ ],
747
+ authentication: [methodId],
748
+ assertionMethod: [methodId],
749
+ capabilityDelegation: [methodId],
750
+ capabilityInvocation: [methodId]
751
+ };
752
+ }
@@ -108,7 +108,8 @@ export function parseContractError(error: any): ParsedContractError {
108
108
  const errorSelector = error.data.slice(0, 10);
109
109
  errorName = ERROR_SELECTORS[errorSelector];
110
110
  } catch (e) {
111
- // Ignore parsing errors
111
+ console.error('[error-parser] failed to parse error selector', e);
112
+ throw e;
112
113
  }
113
114
  }
114
115
 
@@ -0,0 +1,353 @@
1
+ import { ethers } from 'ethers';
2
+ import DID_ABI from '../abi/TerminusDIDABI';
3
+
4
+ // ============================================================
5
+ // Raw Log Type (from eth_getLogs RPC response)
6
+ // ============================================================
7
+
8
+ export interface EthLog {
9
+ address: string;
10
+ topics: string[];
11
+ data: string;
12
+ blockNumber: string; // hex string
13
+ transactionHash: string;
14
+ transactionIndex: string; // hex string
15
+ blockHash: string;
16
+ logIndex: string; // hex string
17
+ removed: boolean;
18
+ }
19
+
20
+ // ============================================================
21
+ // Event Args Types
22
+ // ============================================================
23
+
24
+ /** Transfer (ERC721) — from=0x0 means mint */
25
+ export interface TransferArgs {
26
+ from: string;
27
+ to: string;
28
+ tokenId: bigint;
29
+ }
30
+
31
+ /** Approval (ERC721) */
32
+ export interface ApprovalArgs {
33
+ owner: string;
34
+ approved: string;
35
+ tokenId: bigint;
36
+ }
37
+
38
+ /** ApprovalForAll (ERC721) */
39
+ export interface ApprovalForAllArgs {
40
+ owner: string;
41
+ operator: string;
42
+ approved: boolean;
43
+ }
44
+
45
+ /** Initialized — emitted on proxy initialization */
46
+ export interface InitializedArgs {
47
+ version: bigint;
48
+ }
49
+
50
+ /** NewTagType — emitted when a tag type is defined via defineTag() */
51
+ export interface NewTagTypeArgs {
52
+ domain: string;
53
+ name: string;
54
+ abiType: string; // bytes, hex string
55
+ fieldNamesHash: string[]; // bytes32[]
56
+ }
57
+
58
+ /** OffchainStringArray — emitted to log string[] values off-chain */
59
+ export interface OffchainStringArrayArgs {
60
+ hash: string; // bytes32, indexed
61
+ value: string[];
62
+ }
63
+
64
+ /** OwnershipTransferStarted */
65
+ export interface OwnershipTransferStartedArgs {
66
+ previousOwner: string;
67
+ newOwner: string;
68
+ }
69
+
70
+ /** OwnershipTransferred */
71
+ export interface OwnershipTransferredArgs {
72
+ previousOwner: string;
73
+ newOwner: string;
74
+ }
75
+
76
+ /** TagAdded — emitted when a tag value is set on a domain */
77
+ export interface TagAddedArgs {
78
+ from: string; // domain that owns the tag definition
79
+ to: string; // domain being tagged
80
+ name: string;
81
+ value: string; // bytes, hex string
82
+ }
83
+
84
+ /** TagElemPopped — array element removed from a tag */
85
+ export interface TagElemPoppedArgs {
86
+ from: string;
87
+ to: string;
88
+ name: string;
89
+ elemPath: bigint[]; // uint256[]
90
+ }
91
+
92
+ /** TagElemPushed — array element appended to a tag */
93
+ export interface TagElemPushedArgs {
94
+ from: string;
95
+ to: string;
96
+ name: string;
97
+ elemPath: bigint[]; // uint256[]
98
+ value: string; // bytes, hex string
99
+ }
100
+
101
+ /** TagElemUpdated — array element updated in a tag */
102
+ export interface TagElemUpdatedArgs {
103
+ from: string;
104
+ to: string;
105
+ name: string;
106
+ elemPath: bigint[]; // uint256[]
107
+ value: string; // bytes, hex string
108
+ }
109
+
110
+ /** TagRemoved — tag value removed from a domain */
111
+ export interface TagRemovedArgs {
112
+ from: string;
113
+ to: string;
114
+ name: string;
115
+ }
116
+
117
+ /** TransferByParentOwner — subdomain transferred by parent domain owner */
118
+ export interface TransferByParentOwnerArgs {
119
+ tokenId: bigint;
120
+ }
121
+
122
+ /** TransferBySuperAdmin — domain transferred by super admin */
123
+ export interface TransferBySuperAdminArgs {
124
+ tokenId: bigint;
125
+ }
126
+
127
+ /** Upgraded — UUPS proxy upgraded to new implementation */
128
+ export interface UpgradedArgs {
129
+ implementation: string;
130
+ }
131
+
132
+ // ============================================================
133
+ // Event Name Union & Args Map
134
+ // ============================================================
135
+
136
+ export type TerminusDIDEventName =
137
+ | 'Transfer'
138
+ | 'Approval'
139
+ | 'ApprovalForAll'
140
+ | 'Initialized'
141
+ | 'NewTagType'
142
+ | 'OffchainStringArray'
143
+ | 'OwnershipTransferStarted'
144
+ | 'OwnershipTransferred'
145
+ | 'TagAdded'
146
+ | 'TagElemPopped'
147
+ | 'TagElemPushed'
148
+ | 'TagElemUpdated'
149
+ | 'TagRemoved'
150
+ | 'TransferByParentOwner'
151
+ | 'TransferBySuperAdmin'
152
+ | 'Upgraded';
153
+
154
+ export interface TerminusDIDEventArgsMap {
155
+ Transfer: TransferArgs;
156
+ Approval: ApprovalArgs;
157
+ ApprovalForAll: ApprovalForAllArgs;
158
+ Initialized: InitializedArgs;
159
+ NewTagType: NewTagTypeArgs;
160
+ OffchainStringArray: OffchainStringArrayArgs;
161
+ OwnershipTransferStarted: OwnershipTransferStartedArgs;
162
+ OwnershipTransferred: OwnershipTransferredArgs;
163
+ TagAdded: TagAddedArgs;
164
+ TagElemPopped: TagElemPoppedArgs;
165
+ TagElemPushed: TagElemPushedArgs;
166
+ TagElemUpdated: TagElemUpdatedArgs;
167
+ TagRemoved: TagRemovedArgs;
168
+ TransferByParentOwner: TransferByParentOwnerArgs;
169
+ TransferBySuperAdmin: TransferBySuperAdminArgs;
170
+ Upgraded: UpgradedArgs;
171
+ }
172
+
173
+ // ============================================================
174
+ // Parsed Event Types
175
+ // ============================================================
176
+
177
+ export interface ParsedEvent<T extends TerminusDIDEventName> {
178
+ eventName: T;
179
+ blockNumber: number;
180
+ transactionHash: string;
181
+ logIndex: number;
182
+ tokenId: bigint | undefined;
183
+ args: TerminusDIDEventArgsMap[T];
184
+ raw: EthLog;
185
+ }
186
+
187
+ export type AnyParsedEvent = {
188
+ [K in TerminusDIDEventName]: ParsedEvent<K>;
189
+ }[TerminusDIDEventName];
190
+
191
+ // ============================================================
192
+ // Type Guards
193
+ // ============================================================
194
+
195
+ export function isTransferEvent(
196
+ event: AnyParsedEvent
197
+ ): event is ParsedEvent<'Transfer'> {
198
+ return event.eventName === 'Transfer';
199
+ }
200
+
201
+ export function isMintEvent(
202
+ event: AnyParsedEvent
203
+ ): event is ParsedEvent<'Transfer'> {
204
+ return (
205
+ event.eventName === 'Transfer' &&
206
+ event.args.from === '0x0000000000000000000000000000000000000000'
207
+ );
208
+ }
209
+
210
+ export function isTagAddedEvent(
211
+ event: AnyParsedEvent
212
+ ): event is ParsedEvent<'TagAdded'> {
213
+ return event.eventName === 'TagAdded';
214
+ }
215
+
216
+ export function isTagRemovedEvent(
217
+ event: AnyParsedEvent
218
+ ): event is ParsedEvent<'TagRemoved'> {
219
+ return event.eventName === 'TagRemoved';
220
+ }
221
+
222
+ export function isTagElemUpdatedEvent(
223
+ event: AnyParsedEvent
224
+ ): event is ParsedEvent<'TagElemUpdated'> {
225
+ return event.eventName === 'TagElemUpdated';
226
+ }
227
+
228
+ export function isTagElemPushedEvent(
229
+ event: AnyParsedEvent
230
+ ): event is ParsedEvent<'TagElemPushed'> {
231
+ return event.eventName === 'TagElemPushed';
232
+ }
233
+
234
+ export function isTagElemPoppedEvent(
235
+ event: AnyParsedEvent
236
+ ): event is ParsedEvent<'TagElemPopped'> {
237
+ return event.eventName === 'TagElemPopped';
238
+ }
239
+
240
+ export function isNewTagTypeEvent(
241
+ event: AnyParsedEvent
242
+ ): event is ParsedEvent<'NewTagType'> {
243
+ return event.eventName === 'NewTagType';
244
+ }
245
+
246
+ // ============================================================
247
+ // Parser
248
+ // ============================================================
249
+
250
+ const _iface = new ethers.Interface(DID_ABI.abi);
251
+
252
+ /**
253
+ * Derive tokenId from a domain name, matching the on-chain formula:
254
+ * uint256(keccak256(bytes(domain)))
255
+ */
256
+ function domainToTokenId(domain: string): bigint {
257
+ return BigInt(ethers.keccak256(ethers.toUtf8Bytes(domain)));
258
+ }
259
+
260
+ /**
261
+ * Extract tokenId from event args.
262
+ *
263
+ * ✅ directly in args: Transfer, TransferByParentOwner, TransferBySuperAdmin
264
+ * ✅ derived locally: NewTagType (args.domain), Tag* events (args.to)
265
+ * ❌ not applicable: Approval, ApprovalForAll, Initialized, Upgraded,
266
+ * OwnershipTransferred*, OffchainStringArray
267
+ */
268
+ function extractTokenId(
269
+ eventName: TerminusDIDEventName,
270
+ args: Record<string, unknown>
271
+ ): bigint | undefined {
272
+ switch (eventName) {
273
+ case 'Transfer':
274
+ case 'TransferByParentOwner':
275
+ case 'TransferBySuperAdmin':
276
+ return args['tokenId'] as bigint;
277
+ case 'NewTagType':
278
+ return domainToTokenId(args['domain'] as string);
279
+ case 'TagAdded':
280
+ case 'TagRemoved':
281
+ case 'TagElemUpdated':
282
+ case 'TagElemPushed':
283
+ case 'TagElemPopped':
284
+ return domainToTokenId(args['to'] as string);
285
+ default:
286
+ return undefined;
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Parse raw eth_getLogs entries using the TerminusDID contract ABI.
292
+ *
293
+ * Logs that don't match any known event (e.g. from a different contract)
294
+ * are silently skipped.
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * const logs = await provider.getLogs({ address: CONTRACT_ADDRESS, fromBlock, toBlock });
299
+ * const events = parseEvents(logs);
300
+ * for (const event of events) {
301
+ * if (isTransferEvent(event)) {
302
+ * console.log('Transfer:', event.args.from, '->', event.args.to, event.args.tokenId);
303
+ * }
304
+ * }
305
+ * ```
306
+ */
307
+ export function parseEvents(logs: EthLog[]): AnyParsedEvent[] {
308
+ const result: AnyParsedEvent[] = [];
309
+
310
+ for (const log of logs) {
311
+ try {
312
+ const parsed = _iface.parseLog({
313
+ topics: log.topics,
314
+ data: log.data
315
+ });
316
+ if (!parsed) {
317
+ console.warn(
318
+ '[event-parser] parseLog returned null, matching event cannot be found',
319
+ {
320
+ tx: log.transactionHash,
321
+ logIndex: log.logIndex
322
+ }
323
+ );
324
+ continue;
325
+ }
326
+
327
+ const args: Record<string, unknown> = {};
328
+ parsed.fragment.inputs.forEach((input, idx) => {
329
+ args[input.name] = parsed.args[idx];
330
+ });
331
+
332
+ const eventName = parsed.name as TerminusDIDEventName;
333
+ result.push({
334
+ eventName,
335
+ blockNumber: parseInt(log.blockNumber, 16),
336
+ transactionHash: log.transactionHash,
337
+ logIndex: parseInt(log.logIndex, 16),
338
+ tokenId: extractTokenId(eventName, args),
339
+ args: args as unknown as TerminusDIDEventArgsMap[TerminusDIDEventName],
340
+ raw: log
341
+ } as AnyParsedEvent);
342
+ } catch (err) {
343
+ console.error('[event-parser] failed to parse log', {
344
+ tx: log.transactionHash,
345
+ logIndex: log.logIndex,
346
+ err
347
+ });
348
+ throw err;
349
+ }
350
+ }
351
+
352
+ return result;
353
+ }