@verbeth/sdk 0.1.4 → 0.1.6

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 (190) hide show
  1. package/README.md +20 -168
  2. package/dist/esm/src/addresses.d.ts +20 -0
  3. package/dist/esm/src/addresses.d.ts.map +1 -0
  4. package/dist/esm/src/addresses.js +33 -0
  5. package/dist/esm/src/client/HsrTagIndex.d.ts +77 -0
  6. package/dist/esm/src/client/HsrTagIndex.d.ts.map +1 -0
  7. package/dist/esm/src/client/HsrTagIndex.js +157 -0
  8. package/dist/esm/src/client/PendingManager.d.ts +65 -0
  9. package/dist/esm/src/client/PendingManager.d.ts.map +1 -0
  10. package/dist/esm/src/client/PendingManager.js +84 -0
  11. package/dist/esm/src/client/SessionManager.d.ts +65 -0
  12. package/dist/esm/src/client/SessionManager.d.ts.map +1 -0
  13. package/dist/esm/src/client/SessionManager.js +146 -0
  14. package/dist/esm/src/client/VerbethClient.d.ts +153 -99
  15. package/dist/esm/src/client/VerbethClient.d.ts.map +1 -1
  16. package/dist/esm/src/client/VerbethClient.js +429 -123
  17. package/dist/esm/src/client/VerbethClientBuilder.d.ts +105 -0
  18. package/dist/esm/src/client/VerbethClientBuilder.d.ts.map +1 -0
  19. package/dist/esm/src/client/VerbethClientBuilder.js +146 -0
  20. package/dist/esm/src/client/hsrMatcher.d.ts +22 -0
  21. package/dist/esm/src/client/hsrMatcher.d.ts.map +1 -0
  22. package/dist/esm/src/client/hsrMatcher.js +31 -0
  23. package/dist/esm/src/client/index.d.ts +6 -1
  24. package/dist/esm/src/client/index.d.ts.map +1 -1
  25. package/dist/esm/src/client/index.js +2 -0
  26. package/dist/esm/src/client/types.d.ts +151 -10
  27. package/dist/esm/src/client/types.d.ts.map +1 -1
  28. package/dist/esm/src/crypto(old).d.ts +46 -0
  29. package/dist/esm/src/crypto(old).d.ts.map +1 -0
  30. package/dist/esm/src/crypto(old).js +137 -0
  31. package/dist/esm/src/crypto.d.ts +7 -29
  32. package/dist/esm/src/crypto.d.ts.map +1 -1
  33. package/dist/esm/src/crypto.js +36 -72
  34. package/dist/esm/src/executor.d.ts +17 -18
  35. package/dist/esm/src/executor.d.ts.map +1 -1
  36. package/dist/esm/src/executor.js +54 -70
  37. package/dist/esm/src/handshake.d.ts +51 -0
  38. package/dist/esm/src/handshake.d.ts.map +1 -0
  39. package/dist/esm/src/handshake.js +105 -0
  40. package/dist/esm/src/identity.d.ts +24 -18
  41. package/dist/esm/src/identity.d.ts.map +1 -1
  42. package/dist/esm/src/identity.js +126 -31
  43. package/dist/esm/src/index.d.ts +11 -7
  44. package/dist/esm/src/index.d.ts.map +1 -1
  45. package/dist/esm/src/index.js +10 -7
  46. package/dist/esm/src/payload.d.ts +3 -30
  47. package/dist/esm/src/payload.d.ts.map +1 -1
  48. package/dist/esm/src/payload.js +3 -77
  49. package/dist/esm/src/pq/kem.d.ts +33 -0
  50. package/dist/esm/src/pq/kem.d.ts.map +1 -0
  51. package/dist/esm/src/pq/kem.js +40 -0
  52. package/dist/esm/src/ratchet/auth.d.ts +34 -0
  53. package/dist/esm/src/ratchet/auth.d.ts.map +1 -0
  54. package/dist/esm/src/ratchet/auth.js +88 -0
  55. package/dist/esm/src/ratchet/codec.d.ts +52 -0
  56. package/dist/esm/src/ratchet/codec.d.ts.map +1 -0
  57. package/dist/esm/src/ratchet/codec.js +127 -0
  58. package/dist/esm/src/ratchet/decrypt.d.ts +28 -0
  59. package/dist/esm/src/ratchet/decrypt.d.ts.map +1 -0
  60. package/dist/esm/src/ratchet/decrypt.js +255 -0
  61. package/dist/esm/src/ratchet/encrypt.d.ts +17 -0
  62. package/dist/esm/src/ratchet/encrypt.d.ts.map +1 -0
  63. package/dist/esm/src/ratchet/encrypt.js +78 -0
  64. package/dist/esm/src/ratchet/index.d.ts +8 -0
  65. package/dist/esm/src/ratchet/index.d.ts.map +1 -0
  66. package/dist/esm/src/ratchet/index.js +8 -0
  67. package/dist/esm/src/ratchet/kdf.d.ts +60 -0
  68. package/dist/esm/src/ratchet/kdf.d.ts.map +1 -0
  69. package/dist/esm/src/ratchet/kdf.js +91 -0
  70. package/dist/esm/src/ratchet/session.d.ts +43 -0
  71. package/dist/esm/src/ratchet/session.d.ts.map +1 -0
  72. package/dist/esm/src/ratchet/session.js +139 -0
  73. package/dist/esm/src/ratchet/types.d.ts +168 -0
  74. package/dist/esm/src/ratchet/types.d.ts.map +1 -0
  75. package/dist/esm/src/ratchet/types.js +27 -0
  76. package/dist/esm/src/safeSessionSigner.d.ts +35 -0
  77. package/dist/esm/src/safeSessionSigner.d.ts.map +1 -0
  78. package/dist/esm/src/safeSessionSigner.js +59 -0
  79. package/dist/esm/src/send.d.ts +32 -24
  80. package/dist/esm/src/send.d.ts.map +1 -1
  81. package/dist/esm/src/send.js +84 -39
  82. package/dist/esm/src/types.d.ts +8 -13
  83. package/dist/esm/src/types.d.ts.map +1 -1
  84. package/dist/esm/src/utils/safeSessionSigner.d.ts +23 -0
  85. package/dist/esm/src/utils/safeSessionSigner.d.ts.map +1 -0
  86. package/dist/esm/src/utils/safeSessionSigner.js +59 -0
  87. package/dist/esm/src/utils/txQueue.d.ts +12 -0
  88. package/dist/esm/src/utils/txQueue.d.ts.map +1 -0
  89. package/dist/esm/src/utils/txQueue.js +25 -0
  90. package/dist/esm/src/utils.d.ts +2 -3
  91. package/dist/esm/src/utils.d.ts.map +1 -1
  92. package/dist/esm/src/utils.js +5 -5
  93. package/dist/esm/src/verify.d.ts +9 -25
  94. package/dist/esm/src/verify.d.ts.map +1 -1
  95. package/dist/esm/src/verify.js +49 -50
  96. package/dist/src/addresses.d.ts +20 -0
  97. package/dist/src/addresses.d.ts.map +1 -0
  98. package/dist/src/addresses.js +33 -0
  99. package/dist/src/client/HsrTagIndex.d.ts +77 -0
  100. package/dist/src/client/HsrTagIndex.d.ts.map +1 -0
  101. package/dist/src/client/HsrTagIndex.js +157 -0
  102. package/dist/src/client/PendingManager.d.ts +65 -0
  103. package/dist/src/client/PendingManager.d.ts.map +1 -0
  104. package/dist/src/client/PendingManager.js +84 -0
  105. package/dist/src/client/SessionManager.d.ts +65 -0
  106. package/dist/src/client/SessionManager.d.ts.map +1 -0
  107. package/dist/src/client/SessionManager.js +146 -0
  108. package/dist/src/client/VerbethClient.d.ts +153 -99
  109. package/dist/src/client/VerbethClient.d.ts.map +1 -1
  110. package/dist/src/client/VerbethClient.js +429 -123
  111. package/dist/src/client/VerbethClientBuilder.d.ts +105 -0
  112. package/dist/src/client/VerbethClientBuilder.d.ts.map +1 -0
  113. package/dist/src/client/VerbethClientBuilder.js +146 -0
  114. package/dist/src/client/hsrMatcher.d.ts +22 -0
  115. package/dist/src/client/hsrMatcher.d.ts.map +1 -0
  116. package/dist/src/client/hsrMatcher.js +31 -0
  117. package/dist/src/client/index.d.ts +6 -1
  118. package/dist/src/client/index.d.ts.map +1 -1
  119. package/dist/src/client/index.js +2 -0
  120. package/dist/src/client/types.d.ts +151 -10
  121. package/dist/src/client/types.d.ts.map +1 -1
  122. package/dist/src/crypto(old).d.ts +46 -0
  123. package/dist/src/crypto(old).d.ts.map +1 -0
  124. package/dist/src/crypto(old).js +137 -0
  125. package/dist/src/crypto.d.ts +7 -29
  126. package/dist/src/crypto.d.ts.map +1 -1
  127. package/dist/src/crypto.js +36 -72
  128. package/dist/src/executor.d.ts +17 -18
  129. package/dist/src/executor.d.ts.map +1 -1
  130. package/dist/src/executor.js +54 -70
  131. package/dist/src/handshake.d.ts +51 -0
  132. package/dist/src/handshake.d.ts.map +1 -0
  133. package/dist/src/handshake.js +105 -0
  134. package/dist/src/identity.d.ts +24 -18
  135. package/dist/src/identity.d.ts.map +1 -1
  136. package/dist/src/identity.js +126 -31
  137. package/dist/src/index.d.ts +11 -7
  138. package/dist/src/index.d.ts.map +1 -1
  139. package/dist/src/index.js +10 -7
  140. package/dist/src/payload.d.ts +3 -30
  141. package/dist/src/payload.d.ts.map +1 -1
  142. package/dist/src/payload.js +3 -77
  143. package/dist/src/pq/kem.d.ts +33 -0
  144. package/dist/src/pq/kem.d.ts.map +1 -0
  145. package/dist/src/pq/kem.js +40 -0
  146. package/dist/src/ratchet/auth.d.ts +34 -0
  147. package/dist/src/ratchet/auth.d.ts.map +1 -0
  148. package/dist/src/ratchet/auth.js +88 -0
  149. package/dist/src/ratchet/codec.d.ts +52 -0
  150. package/dist/src/ratchet/codec.d.ts.map +1 -0
  151. package/dist/src/ratchet/codec.js +127 -0
  152. package/dist/src/ratchet/decrypt.d.ts +28 -0
  153. package/dist/src/ratchet/decrypt.d.ts.map +1 -0
  154. package/dist/src/ratchet/decrypt.js +255 -0
  155. package/dist/src/ratchet/encrypt.d.ts +17 -0
  156. package/dist/src/ratchet/encrypt.d.ts.map +1 -0
  157. package/dist/src/ratchet/encrypt.js +78 -0
  158. package/dist/src/ratchet/index.d.ts +8 -0
  159. package/dist/src/ratchet/index.d.ts.map +1 -0
  160. package/dist/src/ratchet/index.js +8 -0
  161. package/dist/src/ratchet/kdf.d.ts +60 -0
  162. package/dist/src/ratchet/kdf.d.ts.map +1 -0
  163. package/dist/src/ratchet/kdf.js +91 -0
  164. package/dist/src/ratchet/session.d.ts +43 -0
  165. package/dist/src/ratchet/session.d.ts.map +1 -0
  166. package/dist/src/ratchet/session.js +139 -0
  167. package/dist/src/ratchet/types.d.ts +168 -0
  168. package/dist/src/ratchet/types.d.ts.map +1 -0
  169. package/dist/src/ratchet/types.js +27 -0
  170. package/dist/src/safeSessionSigner.d.ts +35 -0
  171. package/dist/src/safeSessionSigner.d.ts.map +1 -0
  172. package/dist/src/safeSessionSigner.js +59 -0
  173. package/dist/src/send.d.ts +32 -24
  174. package/dist/src/send.d.ts.map +1 -1
  175. package/dist/src/send.js +84 -39
  176. package/dist/src/types.d.ts +8 -13
  177. package/dist/src/types.d.ts.map +1 -1
  178. package/dist/src/utils/safeSessionSigner.d.ts +23 -0
  179. package/dist/src/utils/safeSessionSigner.d.ts.map +1 -0
  180. package/dist/src/utils/safeSessionSigner.js +59 -0
  181. package/dist/src/utils/txQueue.d.ts +12 -0
  182. package/dist/src/utils/txQueue.d.ts.map +1 -0
  183. package/dist/src/utils/txQueue.js +25 -0
  184. package/dist/src/utils.d.ts +2 -3
  185. package/dist/src/utils.d.ts.map +1 -1
  186. package/dist/src/utils.js +5 -5
  187. package/dist/src/verify.d.ts +9 -25
  188. package/dist/src/verify.d.ts.map +1 -1
  189. package/dist/src/verify.js +49 -50
  190. package/package.json +2 -1
@@ -1,13 +1,14 @@
1
1
  // packages/sdk/src/verify.ts
2
2
  import { getBytes, hexlify, getAddress } from "ethers";
3
- import { decryptAndExtractHandshakeKeys, computeTagFromInitiator, verifyDuplexTopicsChecksum, deriveDuplexTopics } from "./crypto.js";
3
+ import { decryptAndExtractHandshakeKeys, computeHybridTagFromInitiator } from "./crypto.js";
4
+ import { kem } from "./pq/kem.js";
4
5
  import { parseHandshakePayload, parseHandshakeKeys } from "./payload.js";
5
6
  import { makeViemPublicClient, parseBindingMessage, } from "./utils.js";
6
7
  // ============= Handshake Verification =============
7
8
  /**
8
9
  * handshake verification with mandatory identity proof
9
10
  */
10
- export async function verifyHandshakeIdentity(handshakeEvent, provider) {
11
+ export async function verifyHandshakeIdentity(handshakeEvent, provider, ctx) {
11
12
  try {
12
13
  let plaintextPayload = handshakeEvent.plaintextPayload;
13
14
  if (typeof plaintextPayload === "string" &&
@@ -27,13 +28,7 @@ export async function verifyHandshakeIdentity(handshakeEvent, provider) {
27
28
  console.error("Failed to parse unified pubKeys from handshake event");
28
29
  return false;
29
30
  }
30
- // // 6492 awareness
31
- // const dp: any = content.identityProof;
32
- // const sigPrimary: string = dp.signature;
33
- // const sig6492: string | undefined = dp.signature6492 ?? dp.erc6492;
34
- // const uses6492 = hasERC6492Suffix(sigPrimary) || !!sig6492;
35
- // const isContract1271 = await isSmartContract1271(handshakeEvent.sender, provider);
36
- return await verifyIdentityProof(content.identityProof, handshakeEvent.sender, parsedKeys, provider);
31
+ return await verifyIdentityProof(content.identityProof, handshakeEvent.sender, parsedKeys, provider, ctx);
37
32
  }
38
33
  catch (err) {
39
34
  console.error("verifyHandshakeIdentity error:", err);
@@ -44,7 +39,7 @@ export async function verifyHandshakeIdentity(handshakeEvent, provider) {
44
39
  /**
45
40
  * handshake response verification with mandatory identity proof
46
41
  */
47
- export async function verifyHandshakeResponseIdentity(responseEvent, responderIdentityPubKey, initiatorEphemeralSecretKey, provider) {
42
+ export async function verifyHandshakeResponseIdentity(responseEvent, responderIdentityPubKey, initiatorEphemeralSecretKey, provider, ctx) {
48
43
  try {
49
44
  const extractedResponse = decryptAndExtractHandshakeKeys(responseEvent.ciphertext, initiatorEphemeralSecretKey);
50
45
  if (!extractedResponse) {
@@ -55,21 +50,16 @@ export async function verifyHandshakeResponseIdentity(responseEvent, responderId
55
50
  console.error("Identity public key mismatch in handshake response");
56
51
  return false;
57
52
  }
58
- // 6492 awareness
59
53
  const dpAny = extractedResponse.identityProof;
60
54
  if (!dpAny) {
61
55
  console.error("Missing identityProof in handshake response payload");
62
56
  return false;
63
57
  }
64
- // const sigPrimary: string = dpAny.signature;
65
- // const sig6492: string | undefined = dpAny.signature6492 ?? dpAny.erc6492;
66
- // const uses6492 = hasERC6492Suffix(sigPrimary) || !!sig6492;
67
- // const isContract1271 = await isSmartContract1271(responseEvent.responder,provider);
68
58
  const expectedKeys = {
69
59
  identityPubKey: extractedResponse.identityPubKey,
70
60
  signingPubKey: extractedResponse.signingPubKey,
71
61
  };
72
- return await verifyIdentityProof(extractedResponse.identityProof, responseEvent.responder, expectedKeys, provider);
62
+ return await verifyIdentityProof(extractedResponse.identityProof, responseEvent.responder, expectedKeys, provider, ctx);
73
63
  }
74
64
  catch (err) {
75
65
  console.error("verifyHandshakeResponseIdentity error:", err);
@@ -77,31 +67,36 @@ export async function verifyHandshakeResponseIdentity(responseEvent, responderId
77
67
  }
78
68
  }
79
69
  /**
80
- * Verify "IdentityProof" for EOAs and smart accounts.
70
+ * Verify IdentityProof for EOAs and smart accounts.
81
71
  * - Verifies the signature with viem (EOA / ERC-1271 / ERC-6492).
82
72
  * - Parses and checks the expected address and public key against the message content.
83
73
  */
84
- export async function verifyIdentityProof(identityProof, smartAccountAddress, expectedUnifiedKeys, provider) {
74
+ export async function verifyIdentityProof(identityProof, address, expectedUnifiedKeys, provider, ctx) {
85
75
  try {
86
76
  const client = await makeViemPublicClient(provider);
87
- const address = smartAccountAddress;
77
+ const inputAddress = address;
78
+ const parsed = parseBindingMessage(identityProof.message);
79
+ if (!parsed.address) {
80
+ console.error("Parsed address is undefined");
81
+ return false;
82
+ }
83
+ const signerAddress = getAddress(parsed.address);
88
84
  const okSig = await client.verifyMessage({
89
- address,
85
+ address: signerAddress,
90
86
  message: identityProof.message,
91
87
  signature: identityProof.signature,
92
88
  });
93
89
  if (!okSig) {
94
- console.error("Binding signature invalid for address");
90
+ console.error("Binding signature invalid for signer address");
95
91
  return false;
96
92
  }
97
- const parsed = parseBindingMessage(identityProof.message);
98
93
  if (parsed.header && parsed.header !== "VerbEth Key Binding v1") {
99
94
  console.error("Unexpected binding header:", parsed.header);
100
95
  return false;
101
96
  }
102
- if (!parsed.address ||
103
- getAddress(parsed.address) !== getAddress(smartAccountAddress)) {
104
- console.error("Binding message address mismatch");
97
+ if (!parsed.executorSafeAddress ||
98
+ getAddress(parsed.executorSafeAddress) !== getAddress(inputAddress)) {
99
+ console.error("Binding message Safe address mismatch");
105
100
  return false;
106
101
  }
107
102
  const expectedPkX = hexlify(expectedUnifiedKeys.identityPubKey);
@@ -122,7 +117,19 @@ export async function verifyIdentityProof(identityProof, smartAccountAddress, ex
122
117
  console.error("Unexpected version:", parsed.version);
123
118
  return false;
124
119
  }
125
- // if (typeof parsed.chainId === 'number' && parsed.chainId !== currentChainId) return false;
120
+ // anti replay cross chain or cross dapp:
121
+ if (typeof ctx?.chainId === "number") {
122
+ if (typeof parsed.chainId !== "number" || parsed.chainId !== ctx.chainId) {
123
+ console.error("ChainId mismatch");
124
+ return false;
125
+ }
126
+ }
127
+ if (ctx?.rpId) {
128
+ if (!parsed.rpId || parsed.rpId !== ctx.rpId) {
129
+ console.error("RpId mismatch");
130
+ return false;
131
+ }
132
+ }
126
133
  return true;
127
134
  }
128
135
  catch (err) {
@@ -131,8 +138,8 @@ export async function verifyIdentityProof(identityProof, smartAccountAddress, ex
131
138
  }
132
139
  }
133
140
  // ============= Utility Functions =============
134
- export async function verifyAndExtractHandshakeKeys(handshakeEvent, provider) {
135
- const isValid = await verifyHandshakeIdentity(handshakeEvent, provider);
141
+ export async function verifyAndExtractHandshakeKeys(handshakeEvent, provider, ctx) {
142
+ const isValid = await verifyHandshakeIdentity(handshakeEvent, provider, ctx);
136
143
  if (!isValid) {
137
144
  return { isValid: false };
138
145
  }
@@ -145,17 +152,23 @@ export async function verifyAndExtractHandshakeKeys(handshakeEvent, provider) {
145
152
  keys: parsedKeys,
146
153
  };
147
154
  }
148
- export async function verifyAndExtractHandshakeResponseKeys(responseEvent, initiatorEphemeralSecretKey, provider) {
149
- const Rbytes = getBytes(responseEvent.responderEphemeralR); // hex -> Uint8Array
150
- const expectedTag = computeTagFromInitiator(initiatorEphemeralSecretKey, Rbytes);
151
- if (expectedTag !== responseEvent.inResponseTo) {
152
- return { isValid: false };
153
- }
155
+ export async function verifyAndExtractHandshakeResponseKeys(responseEvent, initiatorEphemeralSecretKey, initiatorKemSecretKey, provider, ctx) {
156
+ // Decrypt first to get kemCiphertext
154
157
  const extractedResponse = decryptAndExtractHandshakeKeys(responseEvent.ciphertext, initiatorEphemeralSecretKey);
155
158
  if (!extractedResponse) {
156
159
  return { isValid: false };
157
160
  }
158
- const isValid = await verifyHandshakeResponseIdentity(responseEvent, extractedResponse.identityPubKey, initiatorEphemeralSecretKey, provider);
161
+ if (!extractedResponse.kemCiphertext) {
162
+ return { isValid: false };
163
+ }
164
+ // Decapsulate and verify hybrid tag
165
+ const Rbytes = getBytes(responseEvent.responderEphemeralR);
166
+ const kemSecret = kem.decapsulate(extractedResponse.kemCiphertext, initiatorKemSecretKey);
167
+ const expectedTag = computeHybridTagFromInitiator(initiatorEphemeralSecretKey, Rbytes, kemSecret);
168
+ if (expectedTag !== responseEvent.inResponseTo) {
169
+ return { isValid: false };
170
+ }
171
+ const isValid = await verifyHandshakeResponseIdentity(responseEvent, extractedResponse.identityPubKey, initiatorEphemeralSecretKey, provider, ctx);
159
172
  if (!isValid) {
160
173
  return { isValid: false };
161
174
  }
@@ -165,22 +178,8 @@ export async function verifyAndExtractHandshakeResponseKeys(responseEvent, initi
165
178
  identityPubKey: extractedResponse.identityPubKey,
166
179
  signingPubKey: extractedResponse.signingPubKey,
167
180
  ephemeralPubKey: extractedResponse.ephemeralPubKey,
181
+ kemCiphertext: extractedResponse.kemCiphertext,
168
182
  note: extractedResponse.note,
169
183
  },
170
184
  };
171
185
  }
172
- /**
173
- * Verify and derive duplex topics from a long-term DH secret.
174
- * - Accepts either `tag` (inResponseTo) or a raw salt as KDF input.
175
- * - Recomputes topicOut/topicIn deterministically from the identity DH.
176
- * - If topicInfo is provided (from HSR), also verify the checksum.
177
- * - Used by the initiator after decrypting a HandshakeResponse to confirm responder’s topics.
178
- */
179
- export function verifyDerivedDuplexTopics({ myIdentitySecretKey, theirIdentityPubKey, tag, salt, topicInfo }) {
180
- const s = salt ?? (tag ? getBytes(tag) : undefined);
181
- if (!s)
182
- throw new Error("Provide either salt or inResponseTo");
183
- const { topicOut, topicIn, checksum } = deriveDuplexTopics(myIdentitySecretKey, theirIdentityPubKey, s);
184
- const ok = topicInfo ? verifyDuplexTopicsChecksum(topicOut, topicIn, topicInfo.chk) : undefined;
185
- return { topics: { topicOut, topicIn }, ok };
186
- }
@@ -0,0 +1,20 @@
1
+ export interface ChainConfig {
2
+ verbethProxy: `0x${string}`;
3
+ verbethImpl: `0x${string}`;
4
+ creationBlock: number;
5
+ moduleSetupHelper?: `0x${string}`;
6
+ }
7
+ export declare const VERBETH_CONFIG: ChainConfig;
8
+ export declare const MODULE_SETUP_HELPERS: Record<number, `0x${string}`>;
9
+ export declare function getVerbethAddress(): `0x${string}`;
10
+ export declare function getCreationBlock(): number;
11
+ export declare function getModuleSetupHelper(chainId: number): `0x${string}` | undefined;
12
+ export declare function isModuleSetupSupported(chainId: number): boolean;
13
+ export declare const SCAN_DEFAULTS: {
14
+ readonly INITIAL_SCAN_BLOCKS: 1000;
15
+ readonly MAX_RETRIES: 3;
16
+ readonly MAX_RANGE_PROVIDER: 2000;
17
+ readonly CHUNK_SIZE: 2000;
18
+ readonly REAL_TIME_BUFFER: 3;
19
+ };
20
+ //# sourceMappingURL=addresses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addresses.d.ts","sourceRoot":"","sources":["../../src/addresses.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,KAAK,MAAM,EAAE,CAAC;IAC5B,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;CACnC;AAGD,eAAO,MAAM,cAAc,EAAE,WAInB,CAAC;AAGX,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE,CAGrD,CAAC;AAGX,wBAAgB,iBAAiB,IAAI,KAAK,MAAM,EAAE,CAEjD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,MAAM,EAAE,GAAG,SAAS,CAE/E;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE/D;AAGD,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC"}
@@ -0,0 +1,33 @@
1
+ // packages/sdk/src/addresses.ts
2
+ // Deterministic deployment - same addresses on Base Mainnet & Sepolia
3
+ export const VERBETH_CONFIG = {
4
+ verbethProxy: '0x82C9c5475D63e4C9e959280e9066aBb24973a663',
5
+ verbethImpl: '0x51670aB6eDE1d1B11C654CCA53b7D42080802326',
6
+ creationBlock: 36053269,
7
+ };
8
+ // Per-chain module setup helpers (for Safe session module)
9
+ export const MODULE_SETUP_HELPERS = {
10
+ 8453: '0xc022F74924BDB4b62D830234d89b066359bF67c0', // Base Mainnet
11
+ 84532: '0xbd59Fea46D308eDF3b75C22a6f64AC68feFc731A', // Base Sepolia
12
+ };
13
+ // Helper functions
14
+ export function getVerbethAddress() {
15
+ return VERBETH_CONFIG.verbethProxy;
16
+ }
17
+ export function getCreationBlock() {
18
+ return VERBETH_CONFIG.creationBlock;
19
+ }
20
+ export function getModuleSetupHelper(chainId) {
21
+ return MODULE_SETUP_HELPERS[chainId];
22
+ }
23
+ export function isModuleSetupSupported(chainId) {
24
+ return chainId in MODULE_SETUP_HELPERS;
25
+ }
26
+ // Scanning defaults (chain-agnostic)
27
+ export const SCAN_DEFAULTS = {
28
+ INITIAL_SCAN_BLOCKS: 1000,
29
+ MAX_RETRIES: 3,
30
+ MAX_RANGE_PROVIDER: 2000,
31
+ CHUNK_SIZE: 2000,
32
+ REAL_TIME_BUFFER: 3,
33
+ };
@@ -0,0 +1,77 @@
1
+ export interface PendingContactEntry {
2
+ address: string;
3
+ handshakeEphemeralSecret: Uint8Array;
4
+ kemSecretKey: Uint8Array;
5
+ }
6
+ /**
7
+ * Index for efficient HSR tag matching.
8
+ *
9
+ * When an HSR arrives, we need to find which pending contact it belongs to.
10
+ * This requires computing tag = computeTagFromInitiator(secret, R) for each
11
+ * pending contact until we find a match.
12
+ *
13
+ * This class caches the computed tags per (contact, R) pair, making
14
+ * subsequent lookups O(1) after the first computation.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const hsrIndex = new HsrTagIndex();
19
+ *
20
+ * // When pending contacts change
21
+ * hsrIndex.rebuild(pendingContacts.map(c => ({
22
+ * address: c.address,
23
+ * handshakeEphemeralSecret: c.handshakeEphemeralSecret
24
+ * })));
25
+ *
26
+ * // Matching O(1) after first computation for each R
27
+ * const matchedAddress = hsrIndex.matchByTag(inResponseToTag, R);
28
+ * ```
29
+ */
30
+ export declare class HsrTagIndex {
31
+ private entries;
32
+ private tagToAddress;
33
+ /**
34
+ * Rebuild the index with a new set of pending contacts.
35
+ *
36
+ * Preserves cached tag computations for contacts that remain.
37
+ * Clears entries for contacts no longer in the list.
38
+ *
39
+ * @param contacts - Current pending contacts
40
+ */
41
+ rebuild(contacts: PendingContactEntry[]): void;
42
+ /**
43
+ * Add a single pending contact without full rebuild.
44
+ *
45
+ * @param contact - Contact to add
46
+ */
47
+ addContact(contact: PendingContactEntry): void;
48
+ /**
49
+ * Remove a contact from the index.
50
+ *
51
+ * @param address - Address of contact to remove
52
+ */
53
+ removeContact(address: string): void;
54
+ clear(): void;
55
+ /**
56
+ * Match an HSR by its tag using hybrid (PQ-secure) computation.
57
+ *
58
+ * Decrypts the payload internally to extract kemCiphertext, then
59
+ * decapsulates and computes the hybrid tag for matching.
60
+ *
61
+ * @param inResponseToTag - The tag from the HSR event
62
+ * @param R - Responder's ephemeral public key (from HSR event)
63
+ * @param encryptedPayload - JSON string of the encrypted HSR payload
64
+ * @returns Address of matching contact, or null if no match
65
+ */
66
+ matchByTag(inResponseToTag: `0x${string}`, R: Uint8Array, encryptedPayload: string): string | null;
67
+ /**
68
+ * Get the number of indexed contacts.
69
+ */
70
+ get size(): number;
71
+ /**
72
+ * Check if a contact is in the index.
73
+ */
74
+ hasContact(address: string): boolean;
75
+ private secretsEqual;
76
+ }
77
+ //# sourceMappingURL=HsrTagIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HsrTagIndex.d.ts","sourceRoot":"","sources":["../../../src/client/HsrTagIndex.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB,EAAE,UAAU,CAAC;IACrC,YAAY,EAAE,UAAU,CAAC;CAC1B;AAQD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,YAAY,CAAkC;IAEtD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,IAAI;IAuB9C;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAc9C;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAcpC,KAAK,IAAI,IAAI;IAKb;;;;;;;;;;OAUG;IACH,UAAU,CACR,eAAe,EAAE,KAAK,MAAM,EAAE,EAC9B,CAAC,EAAE,UAAU,EACb,gBAAgB,EAAE,MAAM,GACvB,MAAM,GAAG,IAAI;IAyBhB;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIpC,OAAO,CAAC,YAAY;CAOrB"}
@@ -0,0 +1,157 @@
1
+ // packages/sdk/src/client/HsrTagIndex.ts
2
+ /**
3
+ * Index for O(1) HSR (Handshake Response) matching.
4
+ *
5
+ * Caches computed tags for pending contacts to avoid O(n) loops
6
+ * when matching incoming handshake responses.
7
+ */
8
+ import { computeHybridTagFromInitiator, decryptHandshakeResponse } from '../crypto.js';
9
+ import { kem } from '../pq/kem.js';
10
+ /**
11
+ * Index for efficient HSR tag matching.
12
+ *
13
+ * When an HSR arrives, we need to find which pending contact it belongs to.
14
+ * This requires computing tag = computeTagFromInitiator(secret, R) for each
15
+ * pending contact until we find a match.
16
+ *
17
+ * This class caches the computed tags per (contact, R) pair, making
18
+ * subsequent lookups O(1) after the first computation.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const hsrIndex = new HsrTagIndex();
23
+ *
24
+ * // When pending contacts change
25
+ * hsrIndex.rebuild(pendingContacts.map(c => ({
26
+ * address: c.address,
27
+ * handshakeEphemeralSecret: c.handshakeEphemeralSecret
28
+ * })));
29
+ *
30
+ * // Matching O(1) after first computation for each R
31
+ * const matchedAddress = hsrIndex.matchByTag(inResponseToTag, R);
32
+ * ```
33
+ */
34
+ export class HsrTagIndex {
35
+ constructor() {
36
+ this.entries = new Map();
37
+ this.tagToAddress = new Map();
38
+ }
39
+ /**
40
+ * Rebuild the index with a new set of pending contacts.
41
+ *
42
+ * Preserves cached tag computations for contacts that remain.
43
+ * Clears entries for contacts no longer in the list.
44
+ *
45
+ * @param contacts - Current pending contacts
46
+ */
47
+ rebuild(contacts) {
48
+ const newEntries = new Map();
49
+ for (const contact of contacts) {
50
+ const existing = this.entries.get(contact.address);
51
+ if (existing &&
52
+ this.secretsEqual(existing.secret, contact.handshakeEphemeralSecret) &&
53
+ this.secretsEqual(existing.kemSecretKey, contact.kemSecretKey)) {
54
+ newEntries.set(contact.address, existing);
55
+ }
56
+ else {
57
+ newEntries.set(contact.address, {
58
+ address: contact.address,
59
+ secret: contact.handshakeEphemeralSecret,
60
+ kemSecretKey: contact.kemSecretKey,
61
+ });
62
+ }
63
+ }
64
+ // Keep tagToAddress cache - it's still valid for already computed tags
65
+ this.entries = newEntries;
66
+ }
67
+ /**
68
+ * Add a single pending contact without full rebuild.
69
+ *
70
+ * @param contact - Contact to add
71
+ */
72
+ addContact(contact) {
73
+ const existing = this.entries.get(contact.address);
74
+ if (!existing ||
75
+ !this.secretsEqual(existing.secret, contact.handshakeEphemeralSecret) ||
76
+ !this.secretsEqual(existing.kemSecretKey, contact.kemSecretKey)) {
77
+ this.entries.set(contact.address, {
78
+ address: contact.address,
79
+ secret: contact.handshakeEphemeralSecret,
80
+ kemSecretKey: contact.kemSecretKey,
81
+ });
82
+ }
83
+ }
84
+ /**
85
+ * Remove a contact from the index.
86
+ *
87
+ * @param address - Address of contact to remove
88
+ */
89
+ removeContact(address) {
90
+ const entry = this.entries.get(address);
91
+ if (entry) {
92
+ // Remove any cached tags for this address
93
+ for (const [tag, addr] of this.tagToAddress) {
94
+ if (addr === address) {
95
+ this.tagToAddress.delete(tag);
96
+ }
97
+ }
98
+ this.entries.delete(address);
99
+ }
100
+ }
101
+ clear() {
102
+ this.entries.clear();
103
+ this.tagToAddress.clear();
104
+ }
105
+ /**
106
+ * Match an HSR by its tag using hybrid (PQ-secure) computation.
107
+ *
108
+ * Decrypts the payload internally to extract kemCiphertext, then
109
+ * decapsulates and computes the hybrid tag for matching.
110
+ *
111
+ * @param inResponseToTag - The tag from the HSR event
112
+ * @param R - Responder's ephemeral public key (from HSR event)
113
+ * @param encryptedPayload - JSON string of the encrypted HSR payload
114
+ * @returns Address of matching contact, or null if no match
115
+ */
116
+ matchByTag(inResponseToTag, R, encryptedPayload) {
117
+ // Cache check
118
+ const cachedAddress = this.tagToAddress.get(inResponseToTag);
119
+ if (cachedAddress) {
120
+ return cachedAddress;
121
+ }
122
+ // For each contact: decrypt → extract kemCiphertext → decapsulate → compute hybrid tag
123
+ for (const [address, entry] of this.entries) {
124
+ const decrypted = decryptHandshakeResponse(encryptedPayload, entry.secret);
125
+ if (!decrypted || !decrypted.kemCiphertext)
126
+ continue;
127
+ const kemSecret = kem.decapsulate(decrypted.kemCiphertext, entry.kemSecretKey);
128
+ const tag = computeHybridTagFromInitiator(entry.secret, R, kemSecret);
129
+ this.tagToAddress.set(tag, address);
130
+ if (tag === inResponseToTag) {
131
+ return address;
132
+ }
133
+ }
134
+ return null;
135
+ }
136
+ /**
137
+ * Get the number of indexed contacts.
138
+ */
139
+ get size() {
140
+ return this.entries.size;
141
+ }
142
+ /**
143
+ * Check if a contact is in the index.
144
+ */
145
+ hasContact(address) {
146
+ return this.entries.has(address);
147
+ }
148
+ secretsEqual(a, b) {
149
+ if (a.length !== b.length)
150
+ return false;
151
+ for (let i = 0; i < a.length; i++) {
152
+ if (a[i] !== b[i])
153
+ return false;
154
+ }
155
+ return true;
156
+ }
157
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Internal Pending Message Coordinator.
3
+ *
4
+ * Manages the lifecycle of outbound messages:
5
+ * - Creating pending records before tx submission
6
+ * - Updating status on submission
7
+ * - Matching confirmations by txHash
8
+ * - Cleaning up after confirmation or failure
9
+ */
10
+ import { PendingStore, PendingMessage } from './types.js';
11
+ export interface CreatePendingParams {
12
+ id: string;
13
+ conversationId: string;
14
+ topic: string;
15
+ payloadHex: string;
16
+ plaintext: string;
17
+ sessionStateBefore: string;
18
+ sessionStateAfter: string;
19
+ createdAt: number;
20
+ }
21
+ export declare class PendingManager {
22
+ private store;
23
+ constructor(store: PendingStore);
24
+ /**
25
+ * Create and save a pending message record.
26
+ * Called right before submitting a transaction.
27
+ */
28
+ create(params: CreatePendingParams): Promise<PendingMessage>;
29
+ /**
30
+ * Mark as submitted with transaction hash.
31
+ * Called immediately after tx is broadcast.
32
+ */
33
+ markSubmitted(id: string, txHash: string): Promise<void>;
34
+ /**
35
+ * Mark as failed.
36
+ * Called when tx submission fails.
37
+ * Note: Ratchet slot is already burned (session was committed).
38
+ */
39
+ markFailed(id: string): Promise<void>;
40
+ get(id: string): Promise<PendingMessage | null>;
41
+ getByTxHash(txHash: string): Promise<PendingMessage | null>;
42
+ getByConversation(conversationId: string): Promise<PendingMessage[]>;
43
+ /**
44
+ * Finalize and delete.
45
+ * Called when we see our MessageSent event on-chain.
46
+ *
47
+ * @returns The finalized pending message, or null if not found
48
+ */
49
+ finalize(id: string): Promise<PendingMessage | null>;
50
+ /**
51
+ * Delete a pending message without finalizing.
52
+ * Used for cleanup on failure or cancellation.
53
+ */
54
+ delete(id: string): Promise<void>;
55
+ /**
56
+ * Clean up stale pending messages.
57
+ * Called periodically to remove old failed/stuck records.
58
+ *
59
+ * @param conversationId - Conversation to clean up
60
+ * @param maxAgeMs - Maximum age in milliseconds (default: 24 hours)
61
+ * @returns Number of records cleaned up
62
+ */
63
+ cleanupStale(conversationId: string, maxAgeMs?: number): Promise<number>;
64
+ }
65
+ //# sourceMappingURL=PendingManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PendingManager.d.ts","sourceRoot":"","sources":["../../../src/client/PendingManager.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,cAAc,EAAiB,MAAM,YAAY,CAAC;AAEzE,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY;IAEvC;;;OAGG;IACG,MAAM,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAUlE;;;OAGG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9D;;;;OAIG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAK/C,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAI3D,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAK1E;;;;;OAKG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAU1D;;;OAGG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC;;;;;;;OAOG;IACG,YAAY,CAChB,cAAc,EAAE,MAAM,EACtB,QAAQ,GAAE,MAA4B,GACrC,OAAO,CAAC,MAAM,CAAC;CAcnB"}
@@ -0,0 +1,84 @@
1
+ // packages/sdk/src/client/PendingManager.ts
2
+ export class PendingManager {
3
+ constructor(store) {
4
+ this.store = store;
5
+ }
6
+ /**
7
+ * Create and save a pending message record.
8
+ * Called right before submitting a transaction.
9
+ */
10
+ async create(params) {
11
+ const pending = {
12
+ ...params,
13
+ txHash: null,
14
+ status: 'preparing',
15
+ };
16
+ await this.store.save(pending);
17
+ return pending;
18
+ }
19
+ /**
20
+ * Mark as submitted with transaction hash.
21
+ * Called immediately after tx is broadcast.
22
+ */
23
+ async markSubmitted(id, txHash) {
24
+ await this.store.updateStatus(id, 'submitted', txHash);
25
+ }
26
+ /**
27
+ * Mark as failed.
28
+ * Called when tx submission fails.
29
+ * Note: Ratchet slot is already burned (session was committed).
30
+ */
31
+ async markFailed(id) {
32
+ await this.store.updateStatus(id, 'failed');
33
+ }
34
+ async get(id) {
35
+ return this.store.get(id);
36
+ }
37
+ async getByTxHash(txHash) {
38
+ return this.store.getByTxHash(txHash);
39
+ }
40
+ async getByConversation(conversationId) {
41
+ return this.store.getByConversation(conversationId);
42
+ }
43
+ /**
44
+ * Finalize and delete.
45
+ * Called when we see our MessageSent event on-chain.
46
+ *
47
+ * @returns The finalized pending message, or null if not found
48
+ */
49
+ async finalize(id) {
50
+ const pending = await this.store.get(id);
51
+ if (!pending) {
52
+ return null;
53
+ }
54
+ await this.store.delete(id);
55
+ return pending;
56
+ }
57
+ /**
58
+ * Delete a pending message without finalizing.
59
+ * Used for cleanup on failure or cancellation.
60
+ */
61
+ async delete(id) {
62
+ await this.store.delete(id);
63
+ }
64
+ /**
65
+ * Clean up stale pending messages.
66
+ * Called periodically to remove old failed/stuck records.
67
+ *
68
+ * @param conversationId - Conversation to clean up
69
+ * @param maxAgeMs - Maximum age in milliseconds (default: 24 hours)
70
+ * @returns Number of records cleaned up
71
+ */
72
+ async cleanupStale(conversationId, maxAgeMs = 24 * 60 * 60 * 1000) {
73
+ const pending = await this.store.getByConversation(conversationId);
74
+ const cutoff = Date.now() - maxAgeMs;
75
+ let cleaned = 0;
76
+ for (const p of pending) {
77
+ if (p.createdAt < cutoff) {
78
+ await this.store.delete(p.id);
79
+ cleaned++;
80
+ }
81
+ }
82
+ return cleaned;
83
+ }
84
+ }