@jibidieuw/dexes 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,4013 @@
1
+ import { isHex, padHex, parseEventLogs, toEventSelector, toHex, toBytes, hexToBytes } from 'viem';
2
+ import * as openpgp2 from 'openpgp';
3
+ import pLimit from 'p-limit';
4
+
5
+ // src/utils/0xstr.ts
6
+ var InvalidHexError = class extends Error {
7
+ constructor(input) {
8
+ super(`Invalid hex string: "${input}"`);
9
+ this.code = "INVALID_HEX";
10
+ this.name = "InvalidHexError";
11
+ this.input = input;
12
+ }
13
+ };
14
+ var HexTooLongError = class extends Error {
15
+ constructor(input) {
16
+ super(`Hex string too long to fit in bytes32: "${input}"`);
17
+ this.code = "HEX_TOO_LONG";
18
+ this.name = "HexTooLongError";
19
+ this.input = input;
20
+ }
21
+ };
22
+ var BYTES32_ZERO = "0x" + "00".repeat(32);
23
+ function toBytes32(hex) {
24
+ if (!isHex(hex, { strict: true }) || hex.length > 66) {
25
+ throw new HexTooLongError(hex);
26
+ }
27
+ return padHex(hex, { dir: "left", size: 32 });
28
+ }
29
+ function to0x(hexstr) {
30
+ const s = hexstr.trim().toLowerCase().startsWith("0x") ? hexstr.trim().toLowerCase() : "0x" + hexstr.trim().toLowerCase();
31
+ if (!isHex(s, { strict: true })) {
32
+ throw new InvalidHexError(hexstr);
33
+ }
34
+ return s;
35
+ }
36
+
37
+ // src/abis/FlatFee.ts
38
+ var FlatFee = [{ "type": "receive", "stateMutability": "payable" }, { "type": "function", "name": "authority", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "isConsumingScheduledOp", "inputs": [], "outputs": [{ "name": "", "type": "bytes4", "internalType": "bytes4" }], "stateMutability": "view" }, { "type": "function", "name": "requestedFee", "inputs": [], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "setAuthority", "inputs": [{ "name": "newAuthority", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "updateRequestedFee", "inputs": [{ "name": "newFee", "type": "uint256", "internalType": "uint256" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "withdrawFees", "inputs": [{ "name": "to", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "event", "name": "AuthorityUpdated", "inputs": [{ "name": "authority", "type": "address", "indexed": false, "internalType": "address" }], "anonymous": false }, { "type": "event", "name": "FeesWithdrawn", "inputs": [{ "name": "to", "type": "address", "indexed": true, "internalType": "address" }, { "name": "amount", "type": "uint256", "indexed": false, "internalType": "uint256" }], "anonymous": false }, { "type": "event", "name": "Initialized", "inputs": [{ "name": "version", "type": "uint64", "indexed": false, "internalType": "uint64" }], "anonymous": false }, { "type": "event", "name": "RequestedFeeUpdated", "inputs": [{ "name": "oldFee", "type": "uint256", "indexed": false, "internalType": "uint256" }, { "name": "newFee", "type": "uint256", "indexed": false, "internalType": "uint256" }], "anonymous": false }, { "type": "error", "name": "AccessManagedInvalidAuthority", "inputs": [{ "name": "authority", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "AccessManagedRequiredDelay", "inputs": [{ "name": "caller", "type": "address", "internalType": "address" }, { "name": "delay", "type": "uint32", "internalType": "uint32" }] }, { "type": "error", "name": "AccessManagedUnauthorized", "inputs": [{ "name": "caller", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "FeeRequired", "inputs": [{ "name": "provided", "type": "uint256", "internalType": "uint256" }, { "name": "required", "type": "uint256", "internalType": "uint256" }] }, { "type": "error", "name": "FeesWithdrawalFailed", "inputs": [] }, { "type": "error", "name": "InvalidInitialization", "inputs": [] }, { "type": "error", "name": "NoDirectPaymentsAllowed", "inputs": [] }, { "type": "error", "name": "NoFeesToWithdraw", "inputs": [] }, { "type": "error", "name": "NotInitializing", "inputs": [] }, { "type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": [] }];
39
+
40
+ // src/utils/viemutils.ts
41
+ async function getBlockTimestamp(client, blockNumber) {
42
+ const block = await client.getBlock({ blockNumber });
43
+ return new Date(Number(block.timestamp) * 1e3);
44
+ }
45
+
46
+ // src/flatfee/flatefee.ts
47
+ var FlatFee2 = class {
48
+ /**
49
+ * Creates a new FlatFee instance.
50
+ *
51
+ * @param address The address of the contract implementing IFlatFee.
52
+ * @param client A Viem public client for interacting with the blockchain.
53
+ * @param walletClient Optional Viem wallet client for signing transactions.
54
+ */
55
+ constructor(address, client, walletClient) {
56
+ this._address = address;
57
+ this._client = client;
58
+ this._walletClient = walletClient;
59
+ }
60
+ /*****************************************************************************************************************/
61
+ /* GETTERS AND SETTERS */
62
+ /*****************************************************************************************************************/
63
+ /**
64
+ * Gets the contract address.
65
+ */
66
+ get address() {
67
+ return this._address;
68
+ }
69
+ /**
70
+ * Sets the contract address.
71
+ */
72
+ set address(value) {
73
+ this._address = value;
74
+ }
75
+ /**
76
+ * Gets the Viem public client.
77
+ */
78
+ get client() {
79
+ return this._client;
80
+ }
81
+ /**
82
+ * Sets the Viem public client.
83
+ */
84
+ set client(client) {
85
+ this._client = client;
86
+ }
87
+ /**
88
+ * Gets the Viem wallet client.
89
+ */
90
+ get walletClient() {
91
+ return this._walletClient;
92
+ }
93
+ /**
94
+ * Sets the Viem wallet client.
95
+ */
96
+ set walletClient(value) {
97
+ this._walletClient = value;
98
+ }
99
+ /**
100
+ * Validate that a wallet client is available for write operations.
101
+ * @throws Error if wallet client is not configured
102
+ */
103
+ ensureWalletClient() {
104
+ if (!this._walletClient) {
105
+ throw new Error("WalletClient is required for write operations. Please set walletClient before calling this method.");
106
+ }
107
+ }
108
+ /*****************************************************************************************************************/
109
+ /* WRITE FUNCTIONS */
110
+ /*****************************************************************************************************************/
111
+ /**
112
+ * Updates the requested service fee.
113
+ *
114
+ * @param newFee The new requested fee to be set.
115
+ * @returns The transaction receipt of the update operation.
116
+ */
117
+ async updateRequestedFee(newFee) {
118
+ this.ensureWalletClient();
119
+ const { request } = await this.client.simulateContract({
120
+ address: this.address,
121
+ account: this.walletClient.account,
122
+ abi: FlatFee,
123
+ functionName: "updateRequestedFee",
124
+ args: [
125
+ newFee
126
+ ]
127
+ });
128
+ const txhash = await this.walletClient.writeContract(request);
129
+ return this.client.waitForTransactionReceipt({ hash: txhash });
130
+ }
131
+ /**
132
+ * Withdraws the full contract balance to the specified address.
133
+ * @param to The address to which the fees are withdrawn.
134
+ * @dev This function should be restricted to authorized users.
135
+ */
136
+ async withdrawFees(to) {
137
+ this.ensureWalletClient();
138
+ const { request } = await this.client.simulateContract({
139
+ address: this.address,
140
+ account: this.walletClient.account,
141
+ abi: FlatFee,
142
+ functionName: "withdrawFees",
143
+ args: [
144
+ to
145
+ ]
146
+ });
147
+ const txhash = await this.walletClient.writeContract(request);
148
+ return this.client.waitForTransactionReceipt({ hash: txhash });
149
+ }
150
+ /*****************************************************************************************************************/
151
+ /* READ FUNCTIONS */
152
+ /*****************************************************************************************************************/
153
+ /**
154
+ * Indicate the fee requested by the smart contract to perform its operations.
155
+ * @returns The requested fee in wei.
156
+ */
157
+ async requestedFee() {
158
+ return this.client.readContract({
159
+ address: this.address,
160
+ abi: FlatFee,
161
+ functionName: "requestedFee"
162
+ });
163
+ }
164
+ /*****************************************************************************************************************/
165
+ /* LOG SEARCH FUNCTIONS */
166
+ /*****************************************************************************************************************/
167
+ /**
168
+ * Searches for RequestedFeeUpdated events emitted by the smart contract.
169
+ *
170
+ * @param fromBlock Filter events from this block number. Genesis block if not specified.
171
+ * @param toBlock Filter events up to this block number. Latest block if not specified.
172
+ * @returns The list of RequestedFeeUpdatedLog matching the provided filters.
173
+ */
174
+ async searchRequestedFeeUpdatedLogs(fromBlock, toBlock) {
175
+ const from = fromBlock ?? 0n;
176
+ const to = toBlock ?? await this.client.getBlockNumber();
177
+ const logs = await this.client.getLogs({
178
+ address: this.address,
179
+ event: FlatFee.find((item) => item.type === "event" && item.name === "RequestedFeeUpdated"),
180
+ fromBlock: from,
181
+ toBlock: to
182
+ });
183
+ return Promise.all(logs.map(async (log) => ({
184
+ blockNumber: log.blockNumber,
185
+ blockHash: log.blockHash,
186
+ blockTimestamp: await getBlockTimestamp(this.client, log.blockNumber),
187
+ logIndex: log.logIndex,
188
+ transactionHash: log.transactionHash,
189
+ oldFee: log.args.oldFee,
190
+ newFee: log.args.newFee
191
+ })));
192
+ }
193
+ /**
194
+ * Searches for FeesWithdrawn events emitted by the smart contract.
195
+ *
196
+ * @param recipients Filter by recipient addresses.
197
+ * @param fromBlock Filter events from this block number. Genesis block if not specified.
198
+ * @param toBlock Filter events up to this block number. Latest block if not specified.
199
+ * @returns The list of FeesWithdrawnLog matching the provided filters.
200
+ */
201
+ async searchFeesWithdrawnLogs(recipients, fromBlock, toBlock) {
202
+ const from = fromBlock ?? 0n;
203
+ const to = toBlock ?? await this.client.getBlockNumber();
204
+ const args = recipients ? { to: recipients } : void 0;
205
+ const logs = await this.client.getLogs({
206
+ address: this.address,
207
+ event: FlatFee.find((item) => item.type === "event" && item.name === "FeesWithdrawn"),
208
+ fromBlock: from,
209
+ toBlock: to,
210
+ ...args !== void 0 && { args }
211
+ });
212
+ return Promise.all(logs.map(async (log) => ({
213
+ blockNumber: log.blockNumber,
214
+ blockHash: log.blockHash,
215
+ blockTimestamp: await getBlockTimestamp(this.client, log.blockNumber),
216
+ transactionHash: log.transactionHash,
217
+ logIndex: log.logIndex,
218
+ to: log.args.to,
219
+ amount: log.args.amount
220
+ })));
221
+ }
222
+ /**
223
+ * Extracts FeesWithdrawnLog entries from a transaction receipt.
224
+ * @param receipt The transaction receipt to extract logs from.
225
+ * @returns The list of FeesWithdrawnLog extracted from the receipt.
226
+ */
227
+ async extractFeesWithdrawnLog(receipt) {
228
+ const parsed = parseEventLogs({
229
+ logs: receipt.logs,
230
+ abi: FlatFee,
231
+ eventName: "FeesWithdrawn"
232
+ });
233
+ return Promise.all(parsed.map(async (log) => ({
234
+ blockNumber: log.blockNumber,
235
+ blockHash: log.blockHash,
236
+ blockTimestamp: await getBlockTimestamp(this.client, log.blockNumber),
237
+ logIndex: log.logIndex,
238
+ transactionHash: log.transactionHash,
239
+ to: log.args.to,
240
+ amount: log.args.amount
241
+ })));
242
+ }
243
+ /**
244
+ * Extracts RequestedFeeUpdatedLog entries from a transaction receipt.
245
+ * @param receipt The transaction receipt to extract logs from.
246
+ * @returns The list of RequestedFeeUpdatedLog extracted from the receipt.
247
+ */
248
+ async extractRequestedFeeUpdatedLog(receipt) {
249
+ const parsed = parseEventLogs({
250
+ logs: receipt.logs,
251
+ abi: FlatFee,
252
+ eventName: "RequestedFeeUpdated"
253
+ });
254
+ return Promise.all(parsed.map(async (log) => ({
255
+ blockNumber: log.blockNumber,
256
+ blockHash: log.blockHash,
257
+ blockTimestamp: await getBlockTimestamp(this.client, log.blockNumber),
258
+ transactionHash: log.transactionHash,
259
+ oldFee: log.args.oldFee,
260
+ newFee: log.args.newFee,
261
+ logIndex: log.logIndex
262
+ })));
263
+ }
264
+ };
265
+
266
+ // src/web3pgp/types/types.ts
267
+ var Web3PGPEvents = {
268
+ KeyRegistered: "KeyRegistered",
269
+ KeyUpdated: "KeyUpdated",
270
+ SubkeyAdded: "SubkeyAdded",
271
+ KeyRevoked: "KeyRevoked",
272
+ KeyCertified: "KeyCertified",
273
+ KeyCertificationRevoked: "KeyCertificationRevoked",
274
+ OwnershipChallenged: "OwnershipChallenged",
275
+ OwnershipProved: "OwnershipProved"
276
+ };
277
+
278
+ // src/abis/Web3PGP.ts
279
+ var Web3PGP = [{ "type": "constructor", "inputs": [], "stateMutability": "nonpayable" }, { "type": "receive", "stateMutability": "payable" }, { "type": "function", "name": "UPGRADE_INTERFACE_VERSION", "inputs": [], "outputs": [{ "name": "", "type": "string", "internalType": "string" }], "stateMutability": "view" }, { "type": "function", "name": "addSubkey", "inputs": [{ "name": "primaryKeyFingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "subkeyFingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "openPGPMsg", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "authority", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "certifyKey", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "issuerFingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "keyCertificate", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "challengeOwnership", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "challenge", "type": "bytes32", "internalType": "bytes32" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "exists", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }], "outputs": [{ "name": "isUsed", "type": "bool", "internalType": "bool" }], "stateMutability": "view" }, { "type": "function", "name": "getKeyPublicationBlock", "inputs": [{ "name": "fingerprints", "type": "bytes32[]", "internalType": "bytes32[]" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "getKeyPublicationBlock", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "initialize", "inputs": [{ "name": "fee", "type": "uint256", "internalType": "uint256" }, { "name": "manager", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "initializeUpgrade", "inputs": [], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "isConsumingScheduledOp", "inputs": [], "outputs": [{ "name": "", "type": "bytes4", "internalType": "bytes4" }], "stateMutability": "view" }, { "type": "function", "name": "isSubKey", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }], "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], "stateMutability": "view" }, { "type": "function", "name": "listCertificationRevocations", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "start", "type": "uint256", "internalType": "uint256" }, { "name": "limit", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "listCertifications", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "start", "type": "uint256", "internalType": "uint256" }, { "name": "limit", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "listKeyUpdates", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "start", "type": "uint256", "internalType": "uint256" }, { "name": "limit", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "listRevocations", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "start", "type": "uint256", "internalType": "uint256" }, { "name": "limit", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "listSubkeys", "inputs": [{ "name": "parentKeyFingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "start", "type": "uint256", "internalType": "uint256" }, { "name": "limit", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "bytes32[]", "internalType": "bytes32[]" }], "stateMutability": "view" }, { "type": "function", "name": "parentOf", "inputs": [{ "name": "subkeyFingerprint", "type": "bytes32", "internalType": "bytes32" }], "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], "stateMutability": "view" }, { "type": "function", "name": "proveOwnership", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "challenge", "type": "bytes32", "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "proxiableUUID", "inputs": [], "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], "stateMutability": "view" }, { "type": "function", "name": "register", "inputs": [{ "name": "primaryKeyFingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "subkeyFingerprints", "type": "bytes32[]", "internalType": "bytes32[]" }, { "name": "openPGPMsg", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "requestedFee", "inputs": [], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "revoke", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "revocationCertificate", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "revokeCertification", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "issuerFingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "revocationSignature", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "setAuthority", "inputs": [{ "name": "newAuthority", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "update", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "openPGPMsg", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "updateRequestedFee", "inputs": [{ "name": "newFee", "type": "uint256", "internalType": "uint256" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "upgradeToAndCall", "inputs": [{ "name": "newImplementation", "type": "address", "internalType": "address" }, { "name": "data", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "withdrawFees", "inputs": [{ "name": "to", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "event", "name": "AuthorityUpdated", "inputs": [{ "name": "authority", "type": "address", "indexed": false, "internalType": "address" }], "anonymous": false }, { "type": "event", "name": "FeesWithdrawn", "inputs": [{ "name": "to", "type": "address", "indexed": true, "internalType": "address" }, { "name": "amount", "type": "uint256", "indexed": false, "internalType": "uint256" }], "anonymous": false }, { "type": "event", "name": "Initialized", "inputs": [{ "name": "version", "type": "uint64", "indexed": false, "internalType": "uint64" }], "anonymous": false }, { "type": "event", "name": "KeyCertificationRevoked", "inputs": [{ "name": "fingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "issuer", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "revocationSignature", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "KeyCertified", "inputs": [{ "name": "fingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "issuer", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "keyCertificate", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "KeyRegistered", "inputs": [{ "name": "primaryKeyFingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "subkeyFingerprints", "type": "bytes32[]", "indexed": false, "internalType": "bytes32[]" }, { "name": "openPGPMsg", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "KeyRevoked", "inputs": [{ "name": "fingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "revocationCertificate", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "KeyUpdated", "inputs": [{ "name": "fingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "openPGPMsg", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "OwnershipChallenged", "inputs": [{ "name": "fingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "challenge", "type": "bytes32", "indexed": true, "internalType": "bytes32" }], "anonymous": false }, { "type": "event", "name": "OwnershipProved", "inputs": [{ "name": "fingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "challenge", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "RequestedFeeUpdated", "inputs": [{ "name": "oldFee", "type": "uint256", "indexed": false, "internalType": "uint256" }, { "name": "newFee", "type": "uint256", "indexed": false, "internalType": "uint256" }], "anonymous": false }, { "type": "event", "name": "SubkeyAdded", "inputs": [{ "name": "primaryKeyFingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "subkeyFingerprint", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "openPGPMsg", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "Upgraded", "inputs": [{ "name": "implementation", "type": "address", "indexed": true, "internalType": "address" }], "anonymous": false }, { "type": "error", "name": "AccessManagedInvalidAuthority", "inputs": [{ "name": "authority", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "AccessManagedRequiredDelay", "inputs": [{ "name": "caller", "type": "address", "internalType": "address" }, { "name": "delay", "type": "uint32", "internalType": "uint32" }] }, { "type": "error", "name": "AccessManagedUnauthorized", "inputs": [{ "name": "caller", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "AddressEmptyCode", "inputs": [{ "name": "target", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "AlreadyRegistered", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }] }, { "type": "error", "name": "ERC1967InvalidImplementation", "inputs": [{ "name": "implementation", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "ERC1967NonPayable", "inputs": [] }, { "type": "error", "name": "FailedCall", "inputs": [] }, { "type": "error", "name": "FeeRequired", "inputs": [{ "name": "provided", "type": "uint256", "internalType": "uint256" }, { "name": "required", "type": "uint256", "internalType": "uint256" }] }, { "type": "error", "name": "FeesWithdrawalFailed", "inputs": [] }, { "type": "error", "name": "InvalidInitialization", "inputs": [] }, { "type": "error", "name": "NoDirectPaymentsAllowed", "inputs": [] }, { "type": "error", "name": "NoFeesToWithdraw", "inputs": [] }, { "type": "error", "name": "NotInitializing", "inputs": [] }, { "type": "error", "name": "NotRegistered", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }] }, { "type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": [] }, { "type": "error", "name": "SelfCertificationNotAllowed", "inputs": [] }, { "type": "error", "name": "TargetIsASubkey", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }] }, { "type": "error", "name": "UUPSUnauthorizedCallContext", "inputs": [] }, { "type": "error", "name": "UUPSUnsupportedProxiableUUID", "inputs": [{ "name": "slot", "type": "bytes32", "internalType": "bytes32" }] }];
280
+ var _Web3PGP = class _Web3PGP extends FlatFee2 {
281
+ constructor(address, client, walletClient) {
282
+ super(address, client, walletClient);
283
+ }
284
+ /*****************************************************************************************************************/
285
+ /* READ FUNCTIONS */
286
+ /*****************************************************************************************************************/
287
+ /**
288
+ * Check if a given fingerprint has been used to register a key in the contract.
289
+ * @param fingerprint The fingerprint of the key to check.
290
+ * @return True if the fingerprint has been used to register a key in the contract, false otherwise.
291
+ */
292
+ exists(fingerprint) {
293
+ return this.client.readContract({
294
+ address: this.address,
295
+ abi: Web3PGP,
296
+ functionName: "exists",
297
+ args: [toBytes32(fingerprint)]
298
+ });
299
+ }
300
+ /**
301
+ * Check if a given fingerprint corresponds to a key registered as a subkey in the contract.
302
+ * @param fingerprint The fingerprint of the key to check.
303
+ * @return True if the key is a subkey, false otherwise.
304
+ */
305
+ isSubKey(fingerprint) {
306
+ return this.client.readContract({
307
+ address: this.address,
308
+ abi: Web3PGP,
309
+ functionName: "isSubKey",
310
+ args: [toBytes32(fingerprint)]
311
+ });
312
+ }
313
+ /**
314
+ * Get the fingerprint of the parent key for a given subkey.
315
+ * @param subkeyFingerprint The fingerprint of the subkey.
316
+ * @return The fingerprint of the parent key or zero bytes if there is no parent.
317
+ */
318
+ parentOf(subkeyFingerprint) {
319
+ return this.client.readContract({
320
+ address: this.address,
321
+ abi: Web3PGP,
322
+ functionName: "parentOf",
323
+ args: [toBytes32(subkeyFingerprint)]
324
+ });
325
+ }
326
+ /**
327
+ * Get the block number when a key was published.
328
+ * @param fingerprint The fingerprint of the key to check.
329
+ * @return The block number when the key was published, or 0 if not published.
330
+ */
331
+ getKeyPublicationBlock(fingerprint) {
332
+ return this.client.readContract({
333
+ address: this.address,
334
+ abi: Web3PGP,
335
+ functionName: "getKeyPublicationBlock",
336
+ args: [toBytes32(fingerprint)]
337
+ });
338
+ }
339
+ /**
340
+ * Get the block numbers when multiple keys were published.
341
+ * @param fingerprints The fingerprints of the keys to check.
342
+ * @return An array of block numbers corresponding to each fingerprint in the order they were provided.
343
+ */
344
+ getKeyPublicationBlockBatch(fingerprints) {
345
+ return this.client.readContract({
346
+ address: this.address,
347
+ abi: Web3PGP,
348
+ functionName: "getKeyPublicationBlock",
349
+ args: [fingerprints.map((fp) => toBytes32(fp))]
350
+ });
351
+ }
352
+ /**
353
+ * List the block numbers when updates were published for the given fingerprint.
354
+ * @param fingerprint The fingerprint of the key to check.
355
+ * @param start The starting index in the list of updates.
356
+ * @param limit The maximum number of results to return.
357
+ * @return An array of block numbers when updates were published.
358
+ */
359
+ listKeyUpdates(fingerprint, start, limit) {
360
+ return this.client.readContract({
361
+ address: this.address,
362
+ abi: Web3PGP,
363
+ functionName: "listKeyUpdates",
364
+ args: [toBytes32(fingerprint), start, limit]
365
+ });
366
+ }
367
+ /**
368
+ * List the block numbers when revocation certificates were published for the given fingerprint.
369
+ * @param fingerprint The fingerprint of the key to check.
370
+ * @param start The starting index in the list of revocations.
371
+ * @param limit The maximum number of results to return.
372
+ * @return An array of block numbers when revocation certificates were published.
373
+ */
374
+ listRevocations(fingerprint, start, limit) {
375
+ return this.client.readContract({
376
+ address: this.address,
377
+ abi: Web3PGP,
378
+ functionName: "listRevocations",
379
+ args: [toBytes32(fingerprint), start, limit]
380
+ });
381
+ }
382
+ /**
383
+ * List the fingerprints of subkeys registered under a given parent key.
384
+ * @param parentKeyFingerprint The fingerprint of the parent key to check.
385
+ * @param start The starting index in the list of subkeys.
386
+ * @param limit The maximum number of results to return.
387
+ * @return An array of subkey fingerprints.
388
+ */
389
+ listSubkeys(parentKeyFingerprint, start, limit) {
390
+ return this.client.readContract({
391
+ address: this.address,
392
+ abi: Web3PGP,
393
+ functionName: "listSubkeys",
394
+ args: [toBytes32(parentKeyFingerprint), start, limit]
395
+ });
396
+ }
397
+ /**
398
+ * List the block numbers when key certifications were issued for a given key.
399
+ * @param fingerprint The fingerprint of the key.
400
+ * @param start The starting index in the list of certifications.
401
+ * @param limit The maximum number of results to return.
402
+ * @return An array of block numbers when certifications were issued.
403
+ */
404
+ listCertifications(fingerprint, start, limit) {
405
+ return this.client.readContract({
406
+ address: this.address,
407
+ abi: Web3PGP,
408
+ functionName: "listCertifications",
409
+ args: [toBytes32(fingerprint), start, limit]
410
+ });
411
+ }
412
+ /**
413
+ * List the block numbers when key certification revocations were issued for a given key.
414
+ * @param fingerprint The fingerprint of the key.
415
+ * @param start The starting index in the list of revocations.
416
+ * @param limit The maximum number of results to return.
417
+ * @return An array of block numbers when certification revocations were issued.
418
+ */
419
+ listCertificationRevocations(fingerprint, start, limit) {
420
+ return this.client.readContract({
421
+ address: this.address,
422
+ abi: Web3PGP,
423
+ functionName: "listCertificationRevocations",
424
+ args: [toBytes32(fingerprint), start, limit]
425
+ });
426
+ }
427
+ /*****************************************************************************************************************/
428
+ /* WRITE FUNCTIONS (PAYABLE) */
429
+ /*****************************************************************************************************************/
430
+ /**
431
+ * Register a new primary public key and its optional subkeys.
432
+ * @param primaryKeyFingerprint The declared fingerprint of the primary public key.
433
+ * @param subkeyFingerprints Optional array of declared fingerprints of the subkeys attached to the primary key.
434
+ * @param openPGPMsg A binary OpenPGP message containing the primary key, binding signature, metadata, and subkeys.
435
+ * @return Transaction receipt after registration.
436
+ */
437
+ async register(primaryKeyFingerprint, subkeyFingerprints, openPGPMsg) {
438
+ this.ensureWalletClient();
439
+ const fee = await this.requestedFee();
440
+ const { request } = await this.client.simulateContract({
441
+ address: this.address,
442
+ account: this.walletClient.account,
443
+ abi: Web3PGP,
444
+ functionName: "register",
445
+ args: [
446
+ toBytes32(primaryKeyFingerprint),
447
+ subkeyFingerprints.map((fp) => toBytes32(fp)),
448
+ openPGPMsg
449
+ ],
450
+ value: fee
451
+ });
452
+ const txhash = await this.walletClient.writeContract(request);
453
+ return this.client.waitForTransactionReceipt({ hash: txhash });
454
+ }
455
+ /**
456
+ * Update an existing key with new OpenPGP metadata.
457
+ * @param fingerprint The fingerprint of the key to update.
458
+ * @param openPGPMsg A binary OpenPGP message containing updated key material and signatures.
459
+ * @return Transaction receipt after updating the key.
460
+ */
461
+ async update(fingerprint, openPGPMsg) {
462
+ this.ensureWalletClient();
463
+ const fee = await this.requestedFee();
464
+ const { request } = await this.client.simulateContract({
465
+ address: this.address,
466
+ account: this.walletClient.account,
467
+ abi: Web3PGP,
468
+ functionName: "update",
469
+ args: [toBytes32(fingerprint), openPGPMsg],
470
+ value: fee
471
+ });
472
+ const txhash = await this.walletClient.writeContract(request);
473
+ return this.client.waitForTransactionReceipt({ hash: txhash });
474
+ }
475
+ /**
476
+ * Add a new subkey to an already registered primary key.
477
+ * @param primaryKeyFingerprint The fingerprint of the primary key to which to attach the subkey.
478
+ * @param subkeyFingerprint The fingerprint of the subkey.
479
+ * @param openPGPMsg A binary OpenPGP message containing the subkey and its key binding signatures.
480
+ * @return Transaction receipt after adding the subkey.
481
+ */
482
+ async addSubkey(primaryKeyFingerprint, subkeyFingerprint, openPGPMsg) {
483
+ this.ensureWalletClient();
484
+ const fee = await this.requestedFee();
485
+ const { request } = await this.client.simulateContract({
486
+ address: this.address,
487
+ account: this.walletClient.account,
488
+ abi: Web3PGP,
489
+ functionName: "addSubkey",
490
+ args: [
491
+ toBytes32(primaryKeyFingerprint),
492
+ toBytes32(subkeyFingerprint),
493
+ openPGPMsg
494
+ ],
495
+ value: fee
496
+ });
497
+ const txhash = await this.walletClient.writeContract(request);
498
+ return this.client.waitForTransactionReceipt({ hash: txhash });
499
+ }
500
+ /**
501
+ * Publish a key revocation certificate for a target public key.
502
+ * @param fingerprint The fingerprint of the key to be revoked.
503
+ * @param revocationCertificate The binary OpenPGP message containing the key revocation certificate.
504
+ * @return Transaction receipt after publishing the revocation.
505
+ */
506
+ async revoke(fingerprint, revocationCertificate) {
507
+ this.ensureWalletClient();
508
+ const fee = await this.requestedFee();
509
+ const { request } = await this.client.simulateContract({
510
+ address: this.address,
511
+ account: this.walletClient.account,
512
+ abi: Web3PGP,
513
+ functionName: "revoke",
514
+ args: [
515
+ toBytes32(fingerprint),
516
+ revocationCertificate
517
+ ],
518
+ value: fee
519
+ });
520
+ const txhash = await this.walletClient.writeContract(request);
521
+ return this.client.waitForTransactionReceipt({ hash: txhash });
522
+ }
523
+ /**
524
+ * Certify a key by issuing a key certification signature.
525
+ * @param fingerprint The fingerprint of the key being certified.
526
+ * @param issuerFingerprint The fingerprint of the key issuing the certification.
527
+ * @param keyCertificate A binary OpenPGP signature constituting the key certification.
528
+ * @return Transaction receipt after publishing the certification.
529
+ */
530
+ async certifyKey(fingerprint, issuerFingerprint, keyCertificate) {
531
+ this.ensureWalletClient();
532
+ const fee = await this.requestedFee();
533
+ const { request } = await this.client.simulateContract({
534
+ address: this.address,
535
+ account: this.walletClient.account,
536
+ abi: Web3PGP,
537
+ functionName: "certifyKey",
538
+ args: [
539
+ toBytes32(fingerprint),
540
+ toBytes32(issuerFingerprint),
541
+ keyCertificate
542
+ ],
543
+ value: fee
544
+ });
545
+ const txhash = await this.walletClient.writeContract(request);
546
+ return this.client.waitForTransactionReceipt({ hash: txhash });
547
+ }
548
+ /**
549
+ * Revoke a key certification.
550
+ * @param fingerprint The fingerprint of the key whose certification is being revoked.
551
+ * @param issuerFingerprint The fingerprint of the issuer of the certification to revoke.
552
+ * @param revocationSignature A signature constituting the revocation of the certification.
553
+ * @return Transaction receipt after publishing the revocation.
554
+ */
555
+ async revokeCertification(fingerprint, issuerFingerprint, revocationSignature) {
556
+ this.ensureWalletClient();
557
+ const fee = await this.requestedFee();
558
+ const { request } = await this.client.simulateContract({
559
+ address: this.address,
560
+ account: this.walletClient.account,
561
+ abi: Web3PGP,
562
+ functionName: "revokeCertification",
563
+ args: [
564
+ toBytes32(fingerprint),
565
+ toBytes32(issuerFingerprint),
566
+ revocationSignature
567
+ ],
568
+ value: fee
569
+ });
570
+ const txhash = await this.walletClient.writeContract(request);
571
+ return this.client.waitForTransactionReceipt({ hash: txhash });
572
+ }
573
+ /**
574
+ * Challenge ownership of a public key.
575
+ * @param fingerprint The fingerprint of the key to challenge.
576
+ * @param challengeHash The keccak256 hash of the challenge data sent to the user for signing.
577
+ * @return Transaction receipt after issuing the challenge.
578
+ */
579
+ async challengeOwnership(fingerprint, challengeHash) {
580
+ this.ensureWalletClient();
581
+ const fee = await this.requestedFee();
582
+ const { request } = await this.client.simulateContract({
583
+ address: this.address,
584
+ account: this.walletClient.account,
585
+ abi: Web3PGP,
586
+ functionName: "challengeOwnership",
587
+ args: [
588
+ toBytes32(fingerprint),
589
+ toBytes32(challengeHash)
590
+ ],
591
+ value: fee
592
+ });
593
+ const txhash = await this.walletClient.writeContract(request);
594
+ return this.client.waitForTransactionReceipt({ hash: txhash });
595
+ }
596
+ /**
597
+ * Prove ownership of a public key by responding to a challenge.
598
+ * @param fingerprint The fingerprint of the key.
599
+ * @param challengeHash The keccak256 hash of the challenge data.
600
+ * @param signature A signature made over the challenge data.
601
+ * @return Transaction receipt after proving ownership.
602
+ */
603
+ async proveOwnership(fingerprint, challengeHash, signature) {
604
+ this.ensureWalletClient();
605
+ const fee = await this.requestedFee();
606
+ const { request } = await this.client.simulateContract({
607
+ address: this.address,
608
+ account: this.walletClient.account,
609
+ abi: Web3PGP,
610
+ functionName: "proveOwnership",
611
+ args: [
612
+ toBytes32(fingerprint),
613
+ toBytes32(challengeHash),
614
+ signature
615
+ ],
616
+ value: fee
617
+ });
618
+ const txhash = await this.walletClient.writeContract(request);
619
+ return this.client.waitForTransactionReceipt({ hash: txhash });
620
+ }
621
+ /*****************************************************************************************************************/
622
+ /* LOGS FUNCTIONS */
623
+ /*****************************************************************************************************************/
624
+ /**
625
+ * Get the log of a key registration event using the provided primary key fingerprint and block number.
626
+ *
627
+ * @param primaryKeyFingerprint The fingerprint of the primary key to retrieve the log for.
628
+ * @param blockNumber The block number where the event was emitted.
629
+ * @throws Error if the event log cannot be found.
630
+ * @return The KeyRegisteredLog object containing event details.
631
+ */
632
+ async getKeyRegisteredLog(primaryKeyFingerprint, blockNumber) {
633
+ const logs = await this.searchKeyRegisteredLogs(primaryKeyFingerprint, blockNumber, blockNumber);
634
+ if (logs.length === 1) return logs[0];
635
+ if (logs.length === 0) throw new Error(`KeyRegistered event log not found for primaryKeyFingerprint ${primaryKeyFingerprint} at block ${blockNumber}`);
636
+ throw new Error(`Multiple KeyRegistered logs found for primaryKeyFingerprint ${primaryKeyFingerprint} at block ${blockNumber}`);
637
+ }
638
+ /**
639
+ * Search for KeyRegistered event logs.
640
+ *
641
+ * @param primaryKeyFingerprint The fingerprint(s) of the primary key to search logs for. Default to all keys.
642
+ * @param fromBlock The starting block number of the search range. 'earliest' is used by default. 'pending' is not allowed.
643
+ * @param toBlock The ending block number of the search range. 'latest' is used by default. 'pending' is not allowed.
644
+ * @return An array of KeyRegisteredLog objects matching the search criteria.
645
+ */
646
+ async searchKeyRegisteredLogs(primaryKeyFingerprint, fromBlock, toBlock) {
647
+ if (fromBlock === "pending" || toBlock === "pending") {
648
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
649
+ }
650
+ const from = fromBlock ?? "earliest";
651
+ const to = toBlock ?? "latest";
652
+ const args = primaryKeyFingerprint !== void 0 ? {
653
+ primaryKeyFingerprint: Array.isArray(primaryKeyFingerprint) ? primaryKeyFingerprint.map(toBytes32) : toBytes32(primaryKeyFingerprint)
654
+ } : void 0;
655
+ const logs = await this.client.getLogs({
656
+ strict: true,
657
+ address: this.address,
658
+ event: _Web3PGP.KEY_REGISTERED_EVENT,
659
+ fromBlock: from,
660
+ toBlock: to,
661
+ ...args !== void 0 && { args }
662
+ });
663
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
664
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
665
+ return logs.map((log) => ({
666
+ type: Web3PGPEvents.KeyRegistered,
667
+ blockNumber: log.blockNumber,
668
+ blockHash: log.blockHash,
669
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
670
+ logIndex: log.logIndex,
671
+ transactionHash: log.transactionHash,
672
+ primaryKeyFingerprint: log.args.primaryKeyFingerprint,
673
+ subkeyFingerprints: log.args.subkeyFingerprints,
674
+ openPGPMsg: log.args.openPGPMsg
675
+ }));
676
+ }
677
+ /**
678
+ * Search for KeyUpdated event logs.
679
+ * @param fingerprint The fingerprint(s) of the key to search logs for. Default to all keys.
680
+ * @param fromBlock The starting block number of the search range. 'earliest' is used by default.
681
+ * @param toBlock The ending block number of the search range. 'latest' is used by default.
682
+ * @return An array of KeyUpdatedLog objects matching the search criteria.
683
+ */
684
+ async searchKeyUpdatedLogs(fingerprint, fromBlock, toBlock) {
685
+ if (fromBlock === "pending" || toBlock === "pending") {
686
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
687
+ }
688
+ const from = fromBlock ?? "earliest";
689
+ const to = toBlock ?? "latest";
690
+ const args = fingerprint !== void 0 ? {
691
+ fingerprint: Array.isArray(fingerprint) ? fingerprint.map(toBytes32) : toBytes32(fingerprint)
692
+ } : void 0;
693
+ const logs = await this.client.getLogs({
694
+ strict: true,
695
+ address: this.address,
696
+ event: _Web3PGP.KEY_UPDATED_EVENT,
697
+ fromBlock: from,
698
+ toBlock: to,
699
+ ...args !== void 0 && { args }
700
+ });
701
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
702
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
703
+ return logs.map((log) => ({
704
+ type: Web3PGPEvents.KeyUpdated,
705
+ blockNumber: log.blockNumber,
706
+ blockHash: log.blockHash,
707
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
708
+ logIndex: log.logIndex,
709
+ transactionHash: log.transactionHash,
710
+ fingerprint: log.args.fingerprint,
711
+ openPGPMsg: log.args.openPGPMsg
712
+ }));
713
+ }
714
+ /**
715
+ * Get the log of a subkey addition event using the provided primary key fingerprint, subkey fingerprint, and block number.
716
+ * @param primaryKeyFingerprint The fingerprint of the primary key.
717
+ * @param subkeyFingerprint The fingerprint of the subkey.
718
+ * @param blockNumber The block number where the event was emitted.
719
+ * @throws Error if the event log cannot be found.
720
+ * @return The SubkeyAddedLog object containing event details.
721
+ */
722
+ async getSubkeyAddedLog(primaryKeyFingerprint, subkeyFingerprint, blockNumber) {
723
+ const logs = await this.searchSubkeyAddedLogs(primaryKeyFingerprint, subkeyFingerprint, blockNumber, blockNumber);
724
+ if (logs.length === 1) return logs[0];
725
+ if (logs.length === 0) throw new Error(`SubkeyAdded event log not found for primaryKeyFingerprint ${primaryKeyFingerprint}, subkeyFingerprint ${subkeyFingerprint} at block ${blockNumber}`);
726
+ throw new Error(`Multiple SubkeyAdded logs found for primaryKeyFingerprint ${primaryKeyFingerprint}, subkeyFingerprint ${subkeyFingerprint} at block ${blockNumber}`);
727
+ }
728
+ /**
729
+ * Search for SubkeyAdded event logs.
730
+ * @param primaryKeyFingerprint The fingerprint(s) of the primary key to search logs for. Default to all keys.
731
+ * @param subkeyFingerprint The fingerprint(s) of the subkey to search logs for. Default to all subkeys.
732
+ * @param fromBlock The starting block number of the search range. 'earliest' is used by default. 'pending' is not allowed.
733
+ * @param toBlock The ending block number of the search range. 'latest' is used by default. 'pending' is not allowed.
734
+ * @return An array of SubkeyAddedLog objects matching the search criteria.
735
+ */
736
+ async searchSubkeyAddedLogs(primaryKeyFingerprint, subkeyFingerprint, fromBlock, toBlock) {
737
+ if (fromBlock === "pending" || toBlock === "pending") {
738
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
739
+ }
740
+ const from = fromBlock ?? "earliest";
741
+ const to = toBlock ?? "latest";
742
+ let args = void 0;
743
+ if (primaryKeyFingerprint !== void 0 || subkeyFingerprint !== void 0) {
744
+ args = {};
745
+ if (primaryKeyFingerprint !== void 0) {
746
+ args.primaryKeyFingerprint = Array.isArray(primaryKeyFingerprint) ? primaryKeyFingerprint.map(toBytes32) : toBytes32(primaryKeyFingerprint);
747
+ }
748
+ if (subkeyFingerprint !== void 0) {
749
+ args.subkeyFingerprint = Array.isArray(subkeyFingerprint) ? subkeyFingerprint.map(toBytes32) : toBytes32(subkeyFingerprint);
750
+ }
751
+ }
752
+ const logs = await this.client.getLogs({
753
+ strict: true,
754
+ address: this.address,
755
+ event: _Web3PGP.SUBKEY_ADDED_EVENT,
756
+ fromBlock: from,
757
+ toBlock: to,
758
+ ...args !== void 0 && { args }
759
+ });
760
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
761
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
762
+ return logs.map((log) => ({
763
+ type: Web3PGPEvents.SubkeyAdded,
764
+ blockNumber: log.blockNumber,
765
+ blockHash: log.blockHash,
766
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
767
+ logIndex: log.logIndex,
768
+ transactionHash: log.transactionHash,
769
+ primaryKeyFingerprint: log.args.primaryKeyFingerprint,
770
+ subkeyFingerprint: log.args.subkeyFingerprint,
771
+ openPGPMsg: log.args.openPGPMsg
772
+ }));
773
+ }
774
+ /**
775
+ * Search for KeyRevoked event logs.
776
+ * @param fingerprint The fingerprint(s) of the key to search logs for. Default to all keys.
777
+ * @param fromBlock The starting block number of the search range. 'earliest' is used by default. 'pending' is not allowed.
778
+ * @param toBlock The ending block number of the search range. 'latest' is used by default. 'pending' is not allowed.
779
+ * @return An array of KeyRevokedLog objects matching the search criteria.
780
+ */
781
+ async searchKeyRevokedLogs(fingerprint, fromBlock, toBlock) {
782
+ if (fromBlock === "pending" || toBlock === "pending") {
783
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
784
+ }
785
+ const from = fromBlock ?? "earliest";
786
+ const to = toBlock ?? "latest";
787
+ const args = fingerprint !== void 0 ? {
788
+ fingerprint: Array.isArray(fingerprint) ? fingerprint.map(toBytes32) : toBytes32(fingerprint)
789
+ } : void 0;
790
+ const logs = await this.client.getLogs({
791
+ strict: true,
792
+ address: this.address,
793
+ event: _Web3PGP.KEY_REVOKED_EVENT,
794
+ fromBlock: from,
795
+ toBlock: to,
796
+ ...args !== void 0 && { args }
797
+ });
798
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
799
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
800
+ return logs.map((log) => ({
801
+ type: Web3PGPEvents.KeyRevoked,
802
+ blockNumber: log.blockNumber,
803
+ blockHash: log.blockHash,
804
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
805
+ logIndex: log.logIndex,
806
+ transactionHash: log.transactionHash,
807
+ fingerprint: log.args.fingerprint,
808
+ revocationCertificate: log.args.revocationCertificate
809
+ }));
810
+ }
811
+ /**
812
+ * Search for OwnershipProved event logs.
813
+ * @param fingerprint The fingerprint(s) of the key whose ownership proofs to search for. Default to all keys.
814
+ * @param challenge The challenge(s) whose ownership proofs to search for. Default to all challenges.
815
+ * @param fromBlock Starting block number (inclusive). 'earliest' block is used if not provided. 'pending' is not allowed.
816
+ * @param toBlock Ending block number (inclusive). 'latest' block is used if not provided. 'pending' is not allowed.
817
+ * @returns An array of OwnershipProvedLog objects matching the search criteria.
818
+ */
819
+ async searchOwnershipChallengedLogs(fingerprint, challenge, fromBlock, toBlock) {
820
+ if (fromBlock === "pending" || toBlock === "pending") {
821
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
822
+ }
823
+ const from = fromBlock ?? "earliest";
824
+ const to = toBlock ?? "latest";
825
+ let args = void 0;
826
+ if (fingerprint !== void 0 || challenge !== void 0) {
827
+ args = {};
828
+ if (fingerprint !== void 0) {
829
+ args.fingerprint = Array.isArray(fingerprint) ? fingerprint.map(toBytes32) : toBytes32(fingerprint);
830
+ }
831
+ if (challenge !== void 0) {
832
+ args.challenge = Array.isArray(challenge) ? challenge.map(toBytes32) : toBytes32(challenge);
833
+ }
834
+ }
835
+ const logs = await this.client.getLogs({
836
+ strict: true,
837
+ address: this.address,
838
+ event: _Web3PGP.OWNERSHIP_CHALLENGED_EVENT,
839
+ fromBlock: from,
840
+ toBlock: to,
841
+ ...args !== void 0 && { args }
842
+ });
843
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
844
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
845
+ return logs.map((log) => ({
846
+ type: Web3PGPEvents.OwnershipChallenged,
847
+ blockNumber: log.blockNumber,
848
+ blockHash: log.blockHash,
849
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
850
+ logIndex: log.logIndex,
851
+ transactionHash: log.transactionHash,
852
+ fingerprint: log.args.fingerprint,
853
+ challenge: log.args.challenge
854
+ }));
855
+ }
856
+ /**
857
+ * Search for OwnershipProved event logs.
858
+ * @param fingerprint The fingerprint(s) of the key whose ownership proofs to search for. Default to all keys.
859
+ * @param challenge The challenge(s) associated with the ownership proofs. Default to all challenges.
860
+ * @param fromBlock Starting block number (inclusive). 'earliest' block is used if not provided. 'pending' is not allowed.
861
+ * @param toBlock Ending block number (inclusive). 'latest' block is used if not provided. 'pending' is not allowed.
862
+ * @returns An array of OwnershipProvedLog objects matching the search criteria.
863
+ */
864
+ async searchOwnershipProvedLogs(fingerprint, challenge, fromBlock, toBlock) {
865
+ if (fromBlock === "pending" || toBlock === "pending") {
866
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
867
+ }
868
+ const from = fromBlock ?? "earliest";
869
+ const to = toBlock ?? "latest";
870
+ let args = void 0;
871
+ if (fingerprint !== void 0 || challenge !== void 0) {
872
+ args = {};
873
+ if (fingerprint !== void 0) {
874
+ args.fingerprint = Array.isArray(fingerprint) ? fingerprint.map(toBytes32) : toBytes32(fingerprint);
875
+ }
876
+ if (challenge !== void 0) {
877
+ args.challenge = Array.isArray(challenge) ? challenge.map(toBytes32) : toBytes32(challenge);
878
+ }
879
+ }
880
+ const logs = await this.client.getLogs({
881
+ strict: true,
882
+ address: this.address,
883
+ event: _Web3PGP.OWNERSHIP_PROVED_EVENT,
884
+ fromBlock: from,
885
+ toBlock: to,
886
+ ...args !== void 0 && { args }
887
+ });
888
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
889
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
890
+ return logs.map((log) => ({
891
+ type: Web3PGPEvents.OwnershipProved,
892
+ blockNumber: log.blockNumber,
893
+ blockHash: log.blockHash,
894
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
895
+ logIndex: log.logIndex,
896
+ transactionHash: log.transactionHash,
897
+ fingerprint: log.args.fingerprint,
898
+ challenge: log.args.challenge,
899
+ signature: log.args.signature
900
+ }));
901
+ }
902
+ /**
903
+ * Search for KeyCertified event logs.
904
+ *
905
+ * @param fingerprint The fingerprint(s) of the key being certified. Default to all keys.
906
+ * @param issuerFingerprint The fingerprint(s) of the issuer of the certification. Default to all issuers.
907
+ * @param fromBlock Starting block number (inclusive). 'earliest' block is used if not provided. 'pending' is not allowed.
908
+ * @param toBlock Ending block number (inclusive). 'latest' block is used if not provided. 'pending' is not allowed.
909
+ * @returns An array of KeyCertifiedLog objects matching the search criteria.
910
+ */
911
+ async searchKeyCertifiedLogs(fingerprint, issuerFingerprint, fromBlock, toBlock) {
912
+ if (fromBlock === "pending" || toBlock === "pending") {
913
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
914
+ }
915
+ const from = fromBlock ?? "earliest";
916
+ const to = toBlock ?? "latest";
917
+ let args = void 0;
918
+ if (fingerprint !== void 0 || issuerFingerprint !== void 0) {
919
+ args = {};
920
+ if (fingerprint !== void 0) {
921
+ args.fingerprint = Array.isArray(fingerprint) ? fingerprint.map(toBytes32) : toBytes32(fingerprint);
922
+ }
923
+ if (issuerFingerprint !== void 0) {
924
+ args.issuerFingerprint = Array.isArray(issuerFingerprint) ? issuerFingerprint.map(toBytes32) : toBytes32(issuerFingerprint);
925
+ }
926
+ }
927
+ const logs = await this.client.getLogs({
928
+ strict: true,
929
+ address: this.address,
930
+ event: _Web3PGP.KEY_CERTIFIED_EVENT,
931
+ fromBlock: from,
932
+ toBlock: to,
933
+ ...args !== void 0 && { args }
934
+ });
935
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
936
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
937
+ return logs.map((log) => ({
938
+ type: Web3PGPEvents.KeyCertified,
939
+ blockNumber: log.blockNumber,
940
+ blockHash: log.blockHash,
941
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
942
+ logIndex: log.logIndex,
943
+ transactionHash: log.transactionHash,
944
+ fingerprint: log.args.fingerprint,
945
+ issuerFingerprint: log.args.issuer,
946
+ keyCertificate: log.args.keyCertificate
947
+ }));
948
+ }
949
+ /**
950
+ * Search for KeyCertificationRevoked event logs.
951
+ *
952
+ * @param fingerprint The fingerprint(s) of the key whose certification revocations to search for. Default to all keys.
953
+ * @param issuerFingerprint The fingerprint(s) of the issuers whose certification revocations to search for. Default to all issuers.
954
+ * @param fromBlock Starting block number (inclusive). 'earliest' block is used if not provided. 'pending' is not allowed.
955
+ * @param toBlock Ending block number (inclusive). 'latest' block is used if not provided. 'pending' is not allowed.
956
+ * @returns An array of KeyCertificationRevokedLog objects matching the search criteria.
957
+ */
958
+ async searchKeyCertificationRevokedLogs(fingerprint, issuerFingerprint, fromBlock, toBlock) {
959
+ if (fromBlock === "pending" || toBlock === "pending") {
960
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
961
+ }
962
+ const from = fromBlock ?? "earliest";
963
+ const to = toBlock ?? "latest";
964
+ let args = void 0;
965
+ if (fingerprint !== void 0 || issuerFingerprint !== void 0) {
966
+ args = {};
967
+ if (fingerprint !== void 0) {
968
+ args.fingerprint = Array.isArray(fingerprint) ? fingerprint.map(toBytes32) : toBytes32(fingerprint);
969
+ }
970
+ if (issuerFingerprint !== void 0) {
971
+ args.issuerFingerprint = Array.isArray(issuerFingerprint) ? issuerFingerprint.map(toBytes32) : toBytes32(issuerFingerprint);
972
+ }
973
+ }
974
+ const logs = await this.client.getLogs({
975
+ strict: true,
976
+ address: this.address,
977
+ event: _Web3PGP.KEY_CERTIFICATION_REVOKED_EVENT,
978
+ fromBlock: from,
979
+ toBlock: to,
980
+ ...args !== void 0 && { args }
981
+ });
982
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
983
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
984
+ return logs.map((log) => ({
985
+ type: Web3PGPEvents.KeyCertificationRevoked,
986
+ blockNumber: log.blockNumber,
987
+ blockHash: log.blockHash,
988
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
989
+ logIndex: log.logIndex,
990
+ transactionHash: log.transactionHash,
991
+ fingerprint: log.args.fingerprint,
992
+ issuerFingerprint: log.args.issuer,
993
+ revocationSignature: log.args.revocationSignature
994
+ }));
995
+ }
996
+ /**
997
+ * Searches for all key-related events within a specified block range. Optionally filters by fingerprints.
998
+ *
999
+ * Note: The fingerprints are the subjects of the events (i.e., the keys being registered, updated, revoked, certified, etc.).
1000
+ * Results will also include subkeys added, challenges, and proofs of ownership related to the listed fingerprints.
1001
+ *
1002
+ * @param fingerprints The fingerprint(s) of the keys to filter events for. Can be a single fingerprint or an array. Defaults to all keys if not provided.
1003
+ * @param fromBlock Starting block number (inclusive). Defaults to 'earliest' if not provided. 'pending' is not allowed.
1004
+ * @param toBlock Ending block number (inclusive). Defaults to 'latest' if not provided. 'pending' is not allowed.
1005
+ * @return An array of Web3PGPEventLog.
1006
+ */
1007
+ async searchKeyEvents(fingerprints, fromBlock, toBlock) {
1008
+ if (fromBlock === "pending" || toBlock === "pending") {
1009
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
1010
+ }
1011
+ const from = fromBlock ?? "earliest";
1012
+ const to = toBlock ?? "latest";
1013
+ const eventSelectors = [
1014
+ _Web3PGP.KEY_REGISTERED_EVENT,
1015
+ _Web3PGP.SUBKEY_ADDED_EVENT,
1016
+ _Web3PGP.KEY_REVOKED_EVENT,
1017
+ _Web3PGP.KEY_UPDATED_EVENT,
1018
+ _Web3PGP.OWNERSHIP_CHALLENGED_EVENT,
1019
+ _Web3PGP.OWNERSHIP_PROVED_EVENT,
1020
+ _Web3PGP.KEY_CERTIFIED_EVENT,
1021
+ _Web3PGP.KEY_CERTIFICATION_REVOKED_EVENT
1022
+ ].map((abi) => toEventSelector(abi));
1023
+ const fingerprintsArray = fingerprints !== void 0 ? Array.isArray(fingerprints) ? fingerprints.map((fp) => toBytes32(fp)) : [toBytes32(fingerprints)] : void 0;
1024
+ const rawLogs = await this.client.request({
1025
+ method: "eth_getLogs",
1026
+ params: [{
1027
+ address: this.address,
1028
+ fromBlock: typeof from === "bigint" ? `0x${from.toString(16)}` : from,
1029
+ toBlock: typeof to === "bigint" ? `0x${to.toString(16)}` : to,
1030
+ topics: fingerprintsArray !== void 0 ? [eventSelectors, fingerprintsArray] : [eventSelectors]
1031
+ }]
1032
+ });
1033
+ const parsedLogs = parseEventLogs({
1034
+ abi: Web3PGP,
1035
+ logs: rawLogs,
1036
+ strict: true
1037
+ });
1038
+ const uniqueBlocks = [...new Set(parsedLogs.map((l) => {
1039
+ const blockNum = l.blockNumber;
1040
+ return typeof blockNum === "string" ? BigInt(blockNum) : blockNum;
1041
+ }))];
1042
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1043
+ return parsedLogs.map((log) => {
1044
+ const blockNumber = typeof log.blockNumber === "string" ? BigInt(log.blockNumber) : log.blockNumber;
1045
+ const baseLog = {
1046
+ blockNumber,
1047
+ blockHash: log.blockHash,
1048
+ blockTimestamp: blockTimestamps.get(blockNumber),
1049
+ transactionHash: log.transactionHash
1050
+ };
1051
+ switch (log.eventName) {
1052
+ case "KeyRegistered":
1053
+ return {
1054
+ ...baseLog,
1055
+ type: Web3PGPEvents.KeyRegistered,
1056
+ primaryKeyFingerprint: log.args.primaryKeyFingerprint,
1057
+ subkeyFingerprints: log.args.subkeyFingerprints,
1058
+ openPGPMsg: log.args.openPGPMsg
1059
+ };
1060
+ case "SubkeyAdded":
1061
+ return {
1062
+ ...baseLog,
1063
+ type: Web3PGPEvents.SubkeyAdded,
1064
+ primaryKeyFingerprint: log.args.primaryKeyFingerprint,
1065
+ subkeyFingerprint: log.args.subkeyFingerprint,
1066
+ openPGPMsg: log.args.openPGPMsg
1067
+ };
1068
+ case "KeyRevoked":
1069
+ return {
1070
+ ...baseLog,
1071
+ type: Web3PGPEvents.KeyRevoked,
1072
+ fingerprint: log.args.fingerprint,
1073
+ revocationCertificate: log.args.revocationCertificate
1074
+ };
1075
+ case "KeyUpdated":
1076
+ return {
1077
+ ...baseLog,
1078
+ type: Web3PGPEvents.KeyUpdated,
1079
+ fingerprint: log.args.fingerprint,
1080
+ openPGPMsg: log.args.openPGPMsg
1081
+ };
1082
+ case "OwnershipChallenged":
1083
+ return {
1084
+ ...baseLog,
1085
+ type: Web3PGPEvents.OwnershipChallenged,
1086
+ fingerprint: log.args.fingerprint,
1087
+ challenge: log.args.challenge
1088
+ };
1089
+ case "OwnershipProved":
1090
+ return {
1091
+ ...baseLog,
1092
+ type: Web3PGPEvents.OwnershipProved,
1093
+ fingerprint: log.args.fingerprint,
1094
+ challenge: log.args.challenge,
1095
+ signature: log.args.signature
1096
+ };
1097
+ case "KeyCertified":
1098
+ return {
1099
+ ...baseLog,
1100
+ type: Web3PGPEvents.KeyCertified,
1101
+ fingerprint: log.args.fingerprint,
1102
+ issuerFingerprint: log.args.issuer,
1103
+ keyCertificate: log.args.keyCertificate
1104
+ };
1105
+ case "KeyCertificationRevoked":
1106
+ return {
1107
+ ...baseLog,
1108
+ type: Web3PGPEvents.KeyCertificationRevoked,
1109
+ fingerprint: log.args.fingerprint,
1110
+ issuerFingerprint: log.args.issuer,
1111
+ revocationSignature: log.args.revocationSignature
1112
+ };
1113
+ default:
1114
+ throw new Error(`Unhandled event type: ${log.eventName}`);
1115
+ }
1116
+ });
1117
+ }
1118
+ /**
1119
+ * Extract KeyRegistered event logs from a transaction receipt.
1120
+ * @param receipt The transaction receipt to extract logs from.
1121
+ * @returns An array of KeyRegisteredLog objects extracted from the receipt.
1122
+ */
1123
+ async extractKeyRegisteredLog(receipt) {
1124
+ let parsedLogs = parseEventLogs({
1125
+ abi: Web3PGP,
1126
+ eventName: "KeyRegistered",
1127
+ logs: receipt.logs
1128
+ });
1129
+ const uniqueBlocks = Array.from(new Set(parsedLogs.map((log) => log.blockNumber)));
1130
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1131
+ return parsedLogs.map((log) => ({
1132
+ type: Web3PGPEvents.KeyRegistered,
1133
+ blockNumber: log.blockNumber,
1134
+ blockHash: log.blockHash,
1135
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
1136
+ logIndex: log.logIndex,
1137
+ transactionHash: log.transactionHash,
1138
+ primaryKeyFingerprint: log.args.primaryKeyFingerprint,
1139
+ subkeyFingerprints: log.args.subkeyFingerprints,
1140
+ openPGPMsg: log.args.openPGPMsg
1141
+ }));
1142
+ }
1143
+ /**
1144
+ * Extract KeyUpdated event logs from a transaction receipt.
1145
+ * @param receipt The transaction receipt to extract logs from.
1146
+ * @returns An array of KeyUpdatedLog objects extracted from the receipt.
1147
+ */
1148
+ async extractKeyUpdatedLog(receipt) {
1149
+ let parsedLogs = parseEventLogs({
1150
+ abi: Web3PGP,
1151
+ eventName: "KeyUpdated",
1152
+ logs: receipt.logs
1153
+ });
1154
+ const uniqueBlocks = Array.from(new Set(parsedLogs.map((log) => log.blockNumber)));
1155
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1156
+ return parsedLogs.map((log) => ({
1157
+ type: Web3PGPEvents.KeyUpdated,
1158
+ blockNumber: log.blockNumber,
1159
+ blockHash: log.blockHash,
1160
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
1161
+ logIndex: log.logIndex,
1162
+ transactionHash: log.transactionHash,
1163
+ fingerprint: log.args.fingerprint,
1164
+ openPGPMsg: log.args.openPGPMsg
1165
+ }));
1166
+ }
1167
+ /**
1168
+ * Extract SubkeyAdded event logs from a transaction receipt.
1169
+ * @param receipt The transaction receipt to extract logs from.
1170
+ * @returns An array of SubkeyAddedLog objects extracted from the receipt.
1171
+ */
1172
+ async extractSubkeyAddedLog(receipt) {
1173
+ let parsedLogs = parseEventLogs({
1174
+ abi: Web3PGP,
1175
+ eventName: "SubkeyAdded",
1176
+ logs: receipt.logs
1177
+ });
1178
+ const uniqueBlocks = Array.from(new Set(parsedLogs.map((log) => log.blockNumber)));
1179
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1180
+ return parsedLogs.map((log) => ({
1181
+ type: Web3PGPEvents.SubkeyAdded,
1182
+ blockNumber: log.blockNumber,
1183
+ blockHash: log.blockHash,
1184
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
1185
+ logIndex: log.logIndex,
1186
+ transactionHash: log.transactionHash,
1187
+ primaryKeyFingerprint: log.args.primaryKeyFingerprint,
1188
+ subkeyFingerprint: log.args.subkeyFingerprint,
1189
+ openPGPMsg: log.args.openPGPMsg
1190
+ }));
1191
+ }
1192
+ /**
1193
+ * Extract KeyRevoked event logs from a transaction receipt.
1194
+ * @param receipt The transaction receipt to extract logs from.
1195
+ * @returns An array of KeyRevokedLog objects extracted from the receipt.
1196
+ */
1197
+ async extractKeyRevokedLog(receipt) {
1198
+ let parsedLogs = parseEventLogs({
1199
+ abi: Web3PGP,
1200
+ eventName: "KeyRevoked",
1201
+ logs: receipt.logs
1202
+ });
1203
+ const uniqueBlocks = Array.from(new Set(parsedLogs.map((log) => log.blockNumber)));
1204
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1205
+ return parsedLogs.map((log) => ({
1206
+ type: Web3PGPEvents.KeyRevoked,
1207
+ blockNumber: log.blockNumber,
1208
+ blockHash: log.blockHash,
1209
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
1210
+ logIndex: log.logIndex,
1211
+ transactionHash: log.transactionHash,
1212
+ fingerprint: log.args.fingerprint,
1213
+ revocationCertificate: log.args.revocationCertificate
1214
+ }));
1215
+ }
1216
+ /**
1217
+ * Extract OwnershipChallenged event logs from a transaction receipt.
1218
+ *
1219
+ * @param receipt The transaction receipt to extract logs from.
1220
+ * @returns An array of OwnershipChallengedLog objects extracted from the receipt.
1221
+ */
1222
+ async extractOwnershipChallengedLog(receipt) {
1223
+ let parsedLogs = parseEventLogs({
1224
+ abi: Web3PGP,
1225
+ eventName: "OwnershipChallenged",
1226
+ logs: receipt.logs
1227
+ });
1228
+ const uniqueBlocks = Array.from(new Set(parsedLogs.map((log) => log.blockNumber)));
1229
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1230
+ return parsedLogs.map((log) => ({
1231
+ type: Web3PGPEvents.OwnershipChallenged,
1232
+ blockNumber: log.blockNumber,
1233
+ blockHash: log.blockHash,
1234
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
1235
+ logIndex: log.logIndex,
1236
+ transactionHash: log.transactionHash,
1237
+ fingerprint: log.args.fingerprint,
1238
+ challenge: log.args.challenge
1239
+ }));
1240
+ }
1241
+ /**
1242
+ * Extract OwnershipProved event logs from a transaction receipt.
1243
+ *
1244
+ * @param receipt The transaction receipt to extract logs from.
1245
+ * @returns An array of OwnershipProvedLog objects extracted from the receipt.
1246
+ */
1247
+ async extractOwnershipProvedLog(receipt) {
1248
+ let parsedLogs = parseEventLogs({
1249
+ abi: Web3PGP,
1250
+ eventName: "OwnershipProved",
1251
+ logs: receipt.logs
1252
+ });
1253
+ const uniqueBlocks = Array.from(new Set(parsedLogs.map((log) => log.blockNumber)));
1254
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1255
+ return parsedLogs.map((log) => ({
1256
+ type: Web3PGPEvents.OwnershipProved,
1257
+ blockNumber: log.blockNumber,
1258
+ blockHash: log.blockHash,
1259
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
1260
+ logIndex: log.logIndex,
1261
+ transactionHash: log.transactionHash,
1262
+ fingerprint: log.args.fingerprint,
1263
+ challenge: log.args.challenge,
1264
+ signature: log.args.signature
1265
+ }));
1266
+ }
1267
+ /**
1268
+ * Extract KeyCertified event logs from a transaction receipt.
1269
+ *
1270
+ * @param receipt The transaction receipt to extract logs from.
1271
+ * @returns An array of KeyCertifiedLog objects extracted from the receipt.
1272
+ */
1273
+ async extractKeyCertifiedLog(receipt) {
1274
+ let parsedLogs = parseEventLogs({
1275
+ abi: Web3PGP,
1276
+ eventName: "KeyCertified",
1277
+ logs: receipt.logs
1278
+ });
1279
+ const uniqueBlocks = Array.from(new Set(parsedLogs.map((log) => log.blockNumber)));
1280
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1281
+ return parsedLogs.map((log) => ({
1282
+ type: Web3PGPEvents.KeyCertified,
1283
+ blockNumber: log.blockNumber,
1284
+ blockHash: log.blockHash,
1285
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
1286
+ logIndex: log.logIndex,
1287
+ transactionHash: log.transactionHash,
1288
+ fingerprint: log.args.fingerprint,
1289
+ issuerFingerprint: log.args.issuer,
1290
+ keyCertificate: log.args.keyCertificate
1291
+ }));
1292
+ }
1293
+ /**
1294
+ * Extract KeyCertificationRevoked event logs from a transaction receipt.
1295
+ *
1296
+ * @param receipt The transaction receipt to extract logs from.
1297
+ * @returns An array of KeyCertificationRevokedLog objects extracted from the receipt.
1298
+ */
1299
+ async extractKeyCertificationRevokedLog(receipt) {
1300
+ let parsedLogs = parseEventLogs({
1301
+ abi: Web3PGP,
1302
+ eventName: "KeyCertificationRevoked",
1303
+ logs: receipt.logs
1304
+ });
1305
+ const uniqueBlocks = Array.from(new Set(parsedLogs.map((log) => log.blockNumber)));
1306
+ const blockTimestamps = await this.getBlockTimestamps(uniqueBlocks);
1307
+ return parsedLogs.map((log) => ({
1308
+ type: Web3PGPEvents.KeyCertificationRevoked,
1309
+ blockNumber: log.blockNumber,
1310
+ blockHash: log.blockHash,
1311
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
1312
+ logIndex: log.logIndex,
1313
+ transactionHash: log.transactionHash,
1314
+ fingerprint: log.args.fingerprint,
1315
+ issuerFingerprint: log.args.issuer,
1316
+ revocationSignature: log.args.revocationSignature
1317
+ }));
1318
+ }
1319
+ /*****************************************************************************************************************/
1320
+ /* UTILITY FUNCTIONS */
1321
+ /*****************************************************************************************************************/
1322
+ /**
1323
+ * Get the current block number of the connected blockchain.
1324
+ * @return The current block number as a bigint.
1325
+ */
1326
+ getBlockNumber() {
1327
+ return this.client.getBlockNumber();
1328
+ }
1329
+ /**
1330
+ * Helper to efficiently fetch timestamps for a list of logs.
1331
+ * Deduplicates block lookups to minimize RPC calls.
1332
+ */
1333
+ async getBlockTimestamps(blockNumbers) {
1334
+ const uniqueBlocks = [...new Set(blockNumbers)];
1335
+ return new Map(
1336
+ await Promise.all(uniqueBlocks.map(
1337
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
1338
+ ))
1339
+ );
1340
+ }
1341
+ };
1342
+ _Web3PGP.abi = Web3PGP;
1343
+ // Pre-computed event definitions for efficient log queries
1344
+ _Web3PGP.KEY_REGISTERED_EVENT = Web3PGP.find((item) => item.type === "event" && item.name === "KeyRegistered");
1345
+ _Web3PGP.SUBKEY_ADDED_EVENT = Web3PGP.find((item) => item.type === "event" && item.name === "SubkeyAdded");
1346
+ _Web3PGP.KEY_REVOKED_EVENT = Web3PGP.find((item) => item.type === "event" && item.name === "KeyRevoked");
1347
+ _Web3PGP.KEY_UPDATED_EVENT = Web3PGP.find((item) => item.type === "event" && item.name === "KeyUpdated");
1348
+ _Web3PGP.OWNERSHIP_CHALLENGED_EVENT = Web3PGP.find((item) => item.type === "event" && item.name === "OwnershipChallenged");
1349
+ _Web3PGP.OWNERSHIP_PROVED_EVENT = Web3PGP.find((item) => item.type === "event" && item.name === "OwnershipProved");
1350
+ _Web3PGP.KEY_CERTIFIED_EVENT = Web3PGP.find((item) => item.type === "event" && item.name === "KeyCertified");
1351
+ _Web3PGP.KEY_CERTIFICATION_REVOKED_EVENT = Web3PGP.find((item) => item.type === "event" && item.name === "KeyCertificationRevoked");
1352
+ var Web3PGP2 = _Web3PGP;
1353
+ var OpenPGPUtils = class {
1354
+ /**
1355
+ * Verify the validity of an OpenPGP public key, including its primary key and all subkeys.
1356
+ *
1357
+ * This checks that the keys are properly signed, not expired and not revoked as of the given date.
1358
+ *
1359
+ * @param key The OpenPGP public key to verify
1360
+ * @param date The date to verify against (defaults to now)
1361
+ * @param config Optional OpenPGP configuration
1362
+ *
1363
+ * @throws - Error if the primary key or any subkey is invalid
1364
+ */
1365
+ static async verifyKey(key, date, config) {
1366
+ await key.verifyPrimaryKey(date, void 0, config);
1367
+ for (const subkey of key.getSubkeys()) {
1368
+ await subkey.verify(date, config);
1369
+ }
1370
+ }
1371
+ /**
1372
+ * Prepare a primary key for blockchain publication by creating a copy of its without
1373
+ * private key material and subkeys. The resulting certificate contains only the primary
1374
+ * key with its user IDs for identity verification.
1375
+ *
1376
+ * Can also be used to sanitize certificates retrieved from blockchain: The smart contract
1377
+ * only validates fingerprint uniqueness but not OpenPGP certificate validity and content.
1378
+ *
1379
+ * @param key The key to prepare (can be private or public)
1380
+ * @returns A sanitized primary key certificate ready for blockchain storage
1381
+ */
1382
+ static async sanitizePrimaryKey(key) {
1383
+ const clonedKey = await openpgp2.readKey({ binaryKey: key.toPublic().write() });
1384
+ clonedKey.subkeys = [];
1385
+ return clonedKey;
1386
+ }
1387
+ /**
1388
+ * Prepare a specific subkey for blockchain publication by isolating it and removing
1389
+ * user IDs to prevent identity collisions.
1390
+ *
1391
+ * @description
1392
+ *
1393
+ * The resulting certificate contains the primary key (for signature verification
1394
+ * interoperability) plus exactly one subkey.
1395
+ *
1396
+ * This creates a "subkey certificate" that can be combined with the primary
1397
+ * key certificate using key.update() for full key reconstruction.
1398
+ *
1399
+ * Can also be used to sanitize certificates retrieved from blockchain for security,
1400
+ * as the smart contract only validates fingerprint uniqueness but not OpenPGP
1401
+ * certificate validity. While cryptographic verification of subkeys provides
1402
+ * inherent security, this sanitization ensures clean certificate reconstruction.
1403
+ *
1404
+ * The function does not verify the subkey's validity (signatures, expiration, revocation).
1405
+ *
1406
+ * @param key The key containing the target subkey
1407
+ * @param fingerprint The subkey fingerprint that will be padded (bytes32 format, with or without 0x prefix)
1408
+ * @returns A sanitized subkey certificate ready for blockchain storage
1409
+ * @throws - Error if the specified subkey fingerprint is not found
1410
+ */
1411
+ static async sanitizeSubkey(key, fingerprint) {
1412
+ const targetFingerprint = toBytes32(to0x(fingerprint));
1413
+ const publicKey = key.toPublic();
1414
+ const targetSubkey = publicKey.subkeys.find(
1415
+ (sub) => toBytes32(to0x(sub.getFingerprint())) === targetFingerprint
1416
+ );
1417
+ if (!targetSubkey) {
1418
+ throw new Error(`No subkey with fingerprint ${fingerprint} found in the provided key`);
1419
+ }
1420
+ publicKey.subkeys = [targetSubkey];
1421
+ return publicKey;
1422
+ }
1423
+ /**
1424
+ * Check if a subkey is revoked by verifying its revocation certificates.
1425
+ *
1426
+ * @param sub The subkey to check
1427
+ * @param primaryKey The primary key that may have issued the revocation
1428
+ * @param date The date to check against (defaults to now)
1429
+ *
1430
+ * @returns True if the subkey is revoked, false otherwise
1431
+ */
1432
+ static async isSubkeyRevoked(sub, primaryKey, date = /* @__PURE__ */ new Date()) {
1433
+ if (primaryKey.getFingerprint() !== sub.mainKey.getFingerprint()) {
1434
+ throw new Error("The provided primary key does not own the specified subkey");
1435
+ }
1436
+ if (await primaryKey.isRevoked(void 0, void 0, date)) {
1437
+ return true;
1438
+ }
1439
+ if (sub.revocationSignatures.length === 0) {
1440
+ return false;
1441
+ }
1442
+ const primaryPublicKey = primaryKey.toPublic();
1443
+ for (const revocationSig of sub.revocationSignatures) {
1444
+ try {
1445
+ await revocationSig.verify(
1446
+ primaryPublicKey.keyPacket,
1447
+ // verification key
1448
+ openpgp2.enums.signature.subkeyRevocation,
1449
+ // signature type
1450
+ { key: primaryPublicKey.keyPacket, bind: sub.keyPacket },
1451
+ // subkey revocation context
1452
+ date
1453
+ // verification date
1454
+ );
1455
+ return true;
1456
+ } catch (error) {
1457
+ continue;
1458
+ }
1459
+ }
1460
+ return false;
1461
+ }
1462
+ /**
1463
+ * Check if a key contains private key material, including subkeys.
1464
+ * This checks for the presence of private keys regardless of whether the primary key
1465
+ * has private material or only subkeys do.
1466
+ *
1467
+ * @param key The key to check
1468
+ * @returns True if the key or any subkey contains private key material, false otherwise
1469
+ */
1470
+ static containsPrivateKeyMaterial(key) {
1471
+ if (key.isPrivate()) {
1472
+ return true;
1473
+ } else {
1474
+ for (const subkey of key.getSubkeys()) {
1475
+ if (subkey.keyPacket instanceof openpgp2.SecretSubkeyPacket) {
1476
+ return true;
1477
+ }
1478
+ }
1479
+ }
1480
+ return false;
1481
+ }
1482
+ /**
1483
+ * List all fingerprints of keys that contain private key material.
1484
+ * Includes the primary key if it has private material, and any subkeys with private material.
1485
+ *
1486
+ * @param key The key to analyze
1487
+ * @returns Array of fingerprints for keys containing private material
1488
+ */
1489
+ static listPrivateKeyFingerprints(key) {
1490
+ const fingerprints = [];
1491
+ if (key.isPrivate()) {
1492
+ fingerprints.push(key.getFingerprint());
1493
+ }
1494
+ for (const subkey of key.getSubkeys()) {
1495
+ if (subkey.keyPacket instanceof openpgp2.SecretSubkeyPacket) {
1496
+ fingerprints.push(subkey.getFingerprint());
1497
+ }
1498
+ }
1499
+ return fingerprints;
1500
+ }
1501
+ /**
1502
+ * List all fingerprints of a key, including subkeys
1503
+ *
1504
+ * @param key The key to list fingerprints from
1505
+ * @returns An array of fingerprints
1506
+ */
1507
+ static listAllFingerprints(key) {
1508
+ return [key.getFingerprint(), ...key.getSubkeys().map((sub) => sub.getFingerprint())];
1509
+ }
1510
+ };
1511
+ var Web3PGPServiceError = class extends Error {
1512
+ constructor(message) {
1513
+ super(message);
1514
+ this.name = "Web3PGPServiceError";
1515
+ }
1516
+ };
1517
+ var Web3PGPServiceCriticalError = class extends Web3PGPServiceError {
1518
+ constructor(message, cause) {
1519
+ super(message);
1520
+ this.cause = cause;
1521
+ this.name = "Web3PGPServiceCriticalError";
1522
+ }
1523
+ };
1524
+ var Web3PGPServiceValidationError = class extends Web3PGPServiceError {
1525
+ constructor(message) {
1526
+ super(message);
1527
+ this.name = "Web3PGPServiceValidationError";
1528
+ }
1529
+ };
1530
+ var Web3PGPService = class {
1531
+ /**
1532
+ * Create a new Web3PGPService instance.
1533
+ *
1534
+ * @param web3pgp The low-level Web3PGP contract instance to use for blockchain interactions
1535
+ *
1536
+ * @example
1537
+ * ```typescript
1538
+ * import { Web3PGP } from './web3pgp';
1539
+ * import { Web3PGPService } from './web3pgp.service';
1540
+ *
1541
+ * const web3pgp = new Web3PGP(contractAddress, publicClient, walletClient);
1542
+ * const service = new Web3PGPService(web3pgp);
1543
+ *
1544
+ * // Now use the high-level service
1545
+ * const receipt = await service.register(publicKey);
1546
+ * ```
1547
+ */
1548
+ constructor(web3pgp, options) {
1549
+ this.web3pgp = web3pgp;
1550
+ this.concurrencyLimit = pLimit(options?.concurrencyLimit ?? 10);
1551
+ }
1552
+ /**
1553
+ * Get the underlying Web3PGP contract instance.
1554
+ *
1555
+ * @returns The low-level Web3PGP contract instance
1556
+ *
1557
+ * @remarks
1558
+ * Exposed for advanced use cases where direct contract access is needed.
1559
+ */
1560
+ get contract() {
1561
+ return this.web3pgp;
1562
+ }
1563
+ /*****************************************************************************************************************/
1564
+ /* WRITE OPERATIONS */
1565
+ /*****************************************************************************************************************/
1566
+ /**
1567
+ * Register a new OpenPGP public key (primary key with optional subkeys) on the blockchain.
1568
+ *
1569
+ * @description
1570
+ * This method:
1571
+ * 1. Verifies the provided public key and subkeys have a valid signature, are not expired and not revoked.
1572
+ * 2. Extracts the primary key fingerprint and subkey fingerprints
1573
+ * 3. Ensures the key and its subkeys are not registered on-chain (enforced by smart contract).
1574
+ * 4. Serializes the key into the OpenPGP binary format.
1575
+ * 5. Registers the key on-chain via the Web3PGP contract
1576
+ *
1577
+ * @param key The OpenPGP public key to register (may include subkeys)
1578
+ * @returns Transaction receipt after successful registration
1579
+ *
1580
+ * @throws Error if the key or one of its subkeys are invalid
1581
+ * @throws Error if the key or one of its subkeys are already registered on-chain
1582
+ * @throws Error if wallet client is not configured
1583
+ * @throws Error if transaction fails
1584
+ *
1585
+ * @example
1586
+ * ```typescript
1587
+ * const armoredKey = '-----BEGIN PGP PUBLIC KEY BLOCK-----...';
1588
+ * const publicKey = await openpgp.readKey({ armoredKey });
1589
+ * const receipt = await service.register(publicKey);
1590
+ * console.log(`Key registered at block ${receipt.blockNumber}`);
1591
+ * ```
1592
+ */
1593
+ async register(key) {
1594
+ try {
1595
+ await OpenPGPUtils.verifyKey(key, /* @__PURE__ */ new Date());
1596
+ return this.web3pgp.register(
1597
+ toBytes32(to0x(key.getFingerprint())),
1598
+ key.subkeys.map((subkey) => toBytes32(to0x(subkey.getFingerprint()))),
1599
+ toHex(key.toPublic().write())
1600
+ );
1601
+ } catch (err) {
1602
+ throw new Web3PGPServiceError(`Failed to register the OpenPGP key: ${err}`);
1603
+ }
1604
+ }
1605
+ /**
1606
+ * Update an existing OpenPGP public key on the blockchain to add or revoke user ID packets,
1607
+ * change preferences or update key expiration.
1608
+ *
1609
+ * @description
1610
+ * This method:
1611
+ * 1. Verifies the provided public key has a valid signature, is not expired and is not revoked.
1612
+ * 2. Prunes subkeys from the key (isolating the primary key for metadata updates).
1613
+ * 3. Extracts the primary key fingerprint.
1614
+ * 4. Ensures the key is registered on-chain (enforced by smart contract).
1615
+ * 5. Serializes the key into the OpenPGP binary format.
1616
+ * 6. Calls the update function of the Web3PGP contract to store the updated key on-chain.
1617
+ *
1618
+ * @param key The OpenPGP public key to update (must include primary key).
1619
+ * @returns Transaction receipt after successful update.
1620
+ *
1621
+ * @throws Error if the key is invalid.
1622
+ * @throws Error if the key is not already registered on-chain.
1623
+ * @throws Error if wallet client is not configured.
1624
+ * @throws Error if the transaction fails.
1625
+ *
1626
+ * @example
1627
+ * ```typescript
1628
+ * const armoredKey = '-----BEGIN PGP PUBLIC KEY BLOCK-----...';
1629
+ * const publicKey = await openpgp.readKey({ armoredKey });
1630
+ * const receipt = await service.update(publicKey);
1631
+ * console.log(`Key updated at block ${receipt.blockNumber}`);
1632
+ * ```
1633
+ */
1634
+ async update(key) {
1635
+ try {
1636
+ const pk = await OpenPGPUtils.sanitizePrimaryKey(key);
1637
+ await OpenPGPUtils.verifyKey(key, /* @__PURE__ */ new Date());
1638
+ return this.web3pgp.update(
1639
+ toBytes32(to0x(pk.getFingerprint())),
1640
+ toHex(pk.toPublic().write())
1641
+ );
1642
+ } catch (err) {
1643
+ throw new Web3PGPServiceError(`Failed to update the OpenPGP key: ${err}`);
1644
+ }
1645
+ }
1646
+ /**
1647
+ * Add a new subkey to a primary key registered on the blockchain.
1648
+ *
1649
+ * This method:
1650
+ * 1. Validates the provided key contains the specified subkey
1651
+ * 2. Removes extra subkeys.
1652
+ * 3. Verifies the primary key is registered on-chain (enforced by smart contract)
1653
+ * 4. Verifies the subkey is not registered on-chain (enforced by smart contract)
1654
+ * 5. Extracts the primary key fingerprint
1655
+ * 6. Verifies the provided key and subkey have valid signatures, are not expired and not revoked.
1656
+ * 7. Serializes the key and its subkey into the OpenPGP binary format.
1657
+ * 8. Adds the subkey on-chain via the Web3PGP contract
1658
+ *
1659
+ * @param key The OpenPGP public key containing both the primary key and the new subkey
1660
+ * @param subkeyFingerprint The fingerprint of the specific subkey to add (must exist in the key)
1661
+ * @returns Transaction receipt after successful subkey addition
1662
+ *
1663
+ * @throws Error if the key is invalid or doesn't contain the specified subkey
1664
+ * @throws Error if the primary key is not registered on-chain
1665
+ * @throws Error if the subkey is already registered on-chain
1666
+ * @throws Error if wallet client is not configured
1667
+ * @throws Error if transaction fails
1668
+ *
1669
+ * @example
1670
+ * ```typescript
1671
+ * const armoredKey = '-----BEGIN PGP PUBLIC KEY BLOCK-----...';
1672
+ * const publicKey = await openpgp.readKey({ armoredKey });
1673
+ * const subkeyFp = '0x' + publicKey.subkeys[0].getFingerprint();
1674
+ * const receipt = await service.addSubkey(publicKey, subkeyFp);
1675
+ * ```
1676
+ */
1677
+ async addSubkey(key, subkeyFingerprint) {
1678
+ try {
1679
+ const pk = await OpenPGPUtils.sanitizeSubkey(key, subkeyFingerprint);
1680
+ const sk = pk.getSubkeys()[0];
1681
+ const now = /* @__PURE__ */ new Date();
1682
+ await OpenPGPUtils.verifyKey(pk, now);
1683
+ return this.web3pgp.addSubkey(
1684
+ toBytes32(to0x(pk.getFingerprint())),
1685
+ toBytes32(to0x(subkeyFingerprint)),
1686
+ toHex(pk.toPublic().write())
1687
+ );
1688
+ } catch (err) {
1689
+ throw new Web3PGPServiceError(`Failed to add the subkey: ${err}`);
1690
+ }
1691
+ }
1692
+ /**
1693
+ * Publish a key revocation certificate on the blockchain.
1694
+ *
1695
+ * @description
1696
+ * This method allows users who have revoked their key using standard OpenPGP tools to publish the key revocation
1697
+ * certificate on-chain and inform others that the key is no longer valid.
1698
+ *
1699
+ * The method accepts either a key object with the revocation signature or a standalone revocation certificate
1700
+ * in armored format. In the later case, the method will download and verify the public key from the blockchain
1701
+ * using the provided fingerprint as ID, apply the revocation certificate, verify the key is revoked at present time
1702
+ * and publish the revoked key on-chain.
1703
+ *
1704
+ * When a revoked key is provided, the method will verify the target key or subkey, identified by the provided
1705
+ * fingerprint, is indeed revoked in the key object at the present time and will publish the revoked key on-chain.
1706
+ *
1707
+ * @param keyOrCertificate The revoked OpenPGP public key or a revocation certificate
1708
+ * @param fingerprint The fingerprint of the key being revoked (primary key or subkey)
1709
+ * @returns Transaction receipt after successful revocation publication
1710
+ *
1711
+ * @throws Error if the key doesn't contain a valid revocation signature at the present time
1712
+ * @throws Error if the fingerprint doesn't match any key in the provided key object
1713
+ * @throws Error if the target key is not registered on-chain
1714
+ * @throws Error if wallet client is not configured
1715
+ * @throws Error if transaction fails
1716
+ *
1717
+ * @example
1718
+ * ```typescript
1719
+ * // After revoking the key with OpenPGP tools
1720
+ * const revokedKey = await openpgp.readKey({ armoredKey: revokedArmoredKey });
1721
+ * const fingerprint = '0x' + revokedKey.getFingerprint();
1722
+ * const receipt = await service.revoke(revokedKey, fingerprint);
1723
+ * ```
1724
+ */
1725
+ async revoke(keyOrCertificate, fingerprint) {
1726
+ if (typeof keyOrCertificate === "string") {
1727
+ return this.revokeWithCertificate(keyOrCertificate, fingerprint);
1728
+ } else {
1729
+ return this.revokeWithKey(keyOrCertificate, fingerprint);
1730
+ }
1731
+ }
1732
+ /**
1733
+ * Revoke a key or subkey using a key object containing the revocation signature.
1734
+ *
1735
+ * The method will verify the target key or subkey, identified by the provided fingerprint, is indeed revoked
1736
+ * in the key object at the present time and publish the revoked key on-chain.
1737
+ *
1738
+ * @param key The OpenPGP public key containing the revocation signature
1739
+ * @param fingerprint The fingerprint of the key being revoked
1740
+ * @returns Transaction receipt after successful revocation publication
1741
+ */
1742
+ async revokeWithKey(key, fingerprint) {
1743
+ const normalizedFingerprint = toBytes32(to0x(fingerprint));
1744
+ if (toBytes32(to0x(key.getFingerprint())) === normalizedFingerprint) {
1745
+ const revoked = await OpenPGPUtils.sanitizePrimaryKey(key);
1746
+ if (await revoked.isRevoked()) {
1747
+ return this.web3pgp.revoke(normalizedFingerprint, toHex(revoked.toPublic().write()));
1748
+ } else {
1749
+ throw new Web3PGPServiceValidationError("The provided key does not contain a valid revocation signature.");
1750
+ }
1751
+ } else {
1752
+ const pk = await OpenPGPUtils.sanitizeSubkey(key, fingerprint);
1753
+ if (await OpenPGPUtils.isSubkeyRevoked(
1754
+ pk.subkeys[0],
1755
+ // We are sure this exists from sanitizeSubkey
1756
+ key
1757
+ )) {
1758
+ return this.web3pgp.revoke(normalizedFingerprint, toHex(pk.toPublic().write()));
1759
+ } else {
1760
+ throw new Web3PGPServiceValidationError("The specified subkey does not contain a valid revocation signature.");
1761
+ }
1762
+ }
1763
+ }
1764
+ /**
1765
+ * Revoke a key or subkey using a standalone revocation certificate.
1766
+ *
1767
+ * The method will download and verify the public key from the blockchain using the provided fingerprint as ID,
1768
+ * apply the revocation certificate, verify the key is revoked at present time and publish the revoked key on-chain.
1769
+ *
1770
+ * @param certificate The armored revocation certificate
1771
+ * @param fingerprint The fingerprint of the key being revoked
1772
+ * @returns Transaction receipt after successful revocation publication
1773
+ */
1774
+ async revokeWithCertificate(certificate, fingerprint) {
1775
+ const normalizedFingerprint = toBytes32(to0x(fingerprint));
1776
+ const publicKey = await this.getPublicKey(normalizedFingerprint);
1777
+ const revocationCheckDate = /* @__PURE__ */ new Date();
1778
+ const revoked = await openpgp2.revokeKey({
1779
+ key: publicKey,
1780
+ revocationCertificate: certificate,
1781
+ date: revocationCheckDate,
1782
+ // To be explicit
1783
+ format: "object"
1784
+ });
1785
+ if (toBytes32(to0x(revoked.publicKey.getFingerprint())) === normalizedFingerprint) {
1786
+ const pk = await OpenPGPUtils.sanitizePrimaryKey(revoked.publicKey);
1787
+ if (await pk.isRevoked()) {
1788
+ return this.web3pgp.revoke(normalizedFingerprint, toHex(pk.toPublic().write()));
1789
+ } else {
1790
+ throw new Web3PGPServiceValidationError("The provided key does not contain a valid revocation signature.");
1791
+ }
1792
+ } else {
1793
+ const pk = await OpenPGPUtils.sanitizeSubkey(revoked.publicKey, normalizedFingerprint);
1794
+ if (await OpenPGPUtils.isSubkeyRevoked(pk.subkeys[0], pk, revocationCheckDate)) {
1795
+ return this.web3pgp.revoke(normalizedFingerprint, toHex(pk.write()));
1796
+ } else {
1797
+ throw new Web3PGPServiceValidationError("The specified subkey does not contain a valid revocation signature.");
1798
+ }
1799
+ }
1800
+ }
1801
+ /**
1802
+ * Initiate a challenge to prove ownership of an OpenPGP key registered on-chain.
1803
+ *
1804
+ * This method submits a hash of a random challenge generated by the user to the blockchain. The owner
1805
+ * of the private key corresponding to the public key must later sign the original challenge off-chain
1806
+ * and submit the signature using the `proveOwnership` method to complete the ownership proof process.
1807
+ *
1808
+ * @param fingerprint The fingerprint of the key to challenge ownership for (primary key or subkey)
1809
+ * @param challengeHash The keccak256 hash of the random challenge generated and hashed by the user (32 bytes hex string)
1810
+ * @returns Transaction receipt after successful challenge submission
1811
+ *
1812
+ * @throws Error if the key is not registered on-chain
1813
+ * @throws Error if wallet client is not configured
1814
+ * @throws Error if transaction fails
1815
+ */
1816
+ async challengeOwnership(fingerprint, challengeHash) {
1817
+ return this.web3pgp.challengeOwnership(fingerprint, challengeHash);
1818
+ }
1819
+ /**
1820
+ * Prove ownership of an OpenPGP key by submitting a signature of a previously issued challenge.
1821
+ *
1822
+ * This method verifies the provided signature against the challenge associated with the specified
1823
+ * key fingerprint on-chain. If the signature is valid, ownership is proven.
1824
+ *
1825
+ * The method:
1826
+ * 1. Fetches the up-to-date public key from the blockchain using the provided fingerprint
1827
+ * 2. Verifies the signature of the challenge (the bytes of the hash) using the public key
1828
+ * 3. Submits the ownership proof on-chain via the Web3PGP contract
1829
+ *
1830
+ * @param fingerprint The fingerprint of the key to prove ownership for (primary key or subkey)
1831
+ * @param challengeHash The keccak256 hash of the original challenge that was issued
1832
+ * @param signature The signature of the challenge created using the private key corresponding to the public key
1833
+ * @returns Transaction receipt after successful ownership proof submission
1834
+ *
1835
+ * @throws Error if the key is not registered on-chain
1836
+ * @throws Error if the signature is invalid or the key is revoked or expired at present time
1837
+ * @throws Error if wallet client is not configured
1838
+ * @throws Error if transaction fails
1839
+ */
1840
+ async proveOwnership(fingerprint, challengeHash, signature) {
1841
+ try {
1842
+ const publicKey = await this.getPublicKey(fingerprint);
1843
+ const verificationResult = await openpgp2.verify({
1844
+ message: await openpgp2.createMessage({ binary: toBytes(challengeHash) }),
1845
+ verificationKeys: publicKey,
1846
+ signature
1847
+ });
1848
+ let isValid = false;
1849
+ for (const sig of verificationResult.signatures) {
1850
+ try {
1851
+ await sig.verified;
1852
+ isValid = true;
1853
+ break;
1854
+ } catch (e) {
1855
+ console.debug(`[WEB3PGP SERVICE] Invalid ownership proof signature found and skipped: ${e}`);
1856
+ }
1857
+ }
1858
+ if (!isValid) {
1859
+ throw new Web3PGPServiceValidationError("The provided signature is invalid.");
1860
+ }
1861
+ return this.web3pgp.proveOwnership(fingerprint, challengeHash, toHex(signature.write()));
1862
+ } catch (err) {
1863
+ throw new Web3PGPServiceError(`Failed to prove ownership of the OpenPGP key: ${err}`);
1864
+ }
1865
+ }
1866
+ /**
1867
+ * Publish a third-party certification of an OpenPGP key on the blockchain.
1868
+ *
1869
+ * This method allows a user (the issuer) to certify another user's OpenPGP public key by publishing
1870
+ * the certification on-chain. The certified key is an OpenPGP public key that contains a certification
1871
+ * signature made by the issuer over the target key.
1872
+ *
1873
+ * @description
1874
+ * This method:
1875
+ * 1. Verifies the target key is registered on-chain (enforced by smart contract)
1876
+ * 2. Extracts and verifies the certification signature in the certified key
1877
+ * 3. Serializes the certified key into the OpenPGP binary format.
1878
+ * 4. Publishes the certification on-chain via the Web3PGP contract.
1879
+ *
1880
+ * @param issuer The OpenPGP public key of the issuer certifying the target key
1881
+ * @param certifiedKey The public key containing the certification signature made by the issuer
1882
+ * @returns Transaction receipt after successful certification publication
1883
+ *
1884
+ * @throws Error if the target key is not registered on-chain
1885
+ * @throws Error if the fingerprint doesn't match the fingerprint of the primary key
1886
+ * @throws Error if the certification signature is invalid or not made by the issuer
1887
+ * @throws Error if wallet client is not configured
1888
+ * @throws Error if transaction fails
1889
+ */
1890
+ async certify(issuer, certifiedKey) {
1891
+ try {
1892
+ if (issuer.getFingerprint() === certifiedKey.getFingerprint()) {
1893
+ throw new Web3PGPServiceValidationError("The issuer key must be different from the certified key.");
1894
+ }
1895
+ const pk = await OpenPGPUtils.sanitizePrimaryKey(certifiedKey);
1896
+ let hasValidCertification = false;
1897
+ for (const user of pk.users) {
1898
+ console.debug(`[WEB3PGP SERVICE] Verifying ${user.otherCertifications.length} certifications for user ID "${user.userID?.toString()}"...`);
1899
+ const validCertifications = [];
1900
+ for (const cert of user.otherCertifications) {
1901
+ try {
1902
+ console.debug(`[WEB3PGP SERVICE] Verifying certification made by ${cert.issuerFingerprint ? toHex(cert.issuerFingerprint) : cert.issuerKeyID.toHex()}...`);
1903
+ const verified = await user.verifyCertificate(cert, [issuer]);
1904
+ if (verified) {
1905
+ validCertifications.push(cert);
1906
+ hasValidCertification = true;
1907
+ } else {
1908
+ console.debug(`[WEB3PGP SERVICE] Invalid certification signature found and skipped.`);
1909
+ }
1910
+ } catch (e) {
1911
+ console.debug(`[WEB3PGP SERVICE] Invalid certification signature found and skipped: ${e}`);
1912
+ }
1913
+ }
1914
+ user.otherCertifications = validCertifications;
1915
+ }
1916
+ if (!hasValidCertification) {
1917
+ throw new Web3PGPServiceValidationError("The certified key does not contain a valid certification signature made by the issuer.");
1918
+ }
1919
+ pk.users = pk.users.filter((user) => user.otherCertifications.length > 0);
1920
+ return this.web3pgp.certifyKey(
1921
+ toBytes32(to0x(pk.getFingerprint())),
1922
+ toBytes32(to0x(issuer.getFingerprint())),
1923
+ toHex(pk.toPublic().write())
1924
+ );
1925
+ } catch (err) {
1926
+ throw new Web3PGPServiceError(`Failed to certify the OpenPGP key: ${err}`);
1927
+ }
1928
+ }
1929
+ /**
1930
+ * Revoke a third-party certification of an OpenPGP key on the blockchain.
1931
+ *
1932
+ * This method allows a user (the issuer) to revoke a previously published certification of another user's
1933
+ * OpenPGP public key by publishing a revocation on-chain. The revoked key is an OpenPGP public key that
1934
+ * contains a revocation signature made by the issuer over the target key.
1935
+ *
1936
+ * @description
1937
+ * This method:
1938
+ * 1. Verifies the target key is registered on-chain (enforced by smart contract)
1939
+ * 2. Extracts and verifies the revoked third-party signature in the key.
1940
+ * 3. Serializes the certified key into the OpenPGP binary format.
1941
+ * 4. Publishes the certification revocation on-chain via the Web3PGP contract.
1942
+ *
1943
+ * @param issuer The OpenPGP public key of the issuer revoking the certification
1944
+ * @param keyWithRevokedCertification The public key containing the revocation signature made by the issuer
1945
+ * @returns Transaction receipt after successful revocation publication
1946
+ *
1947
+ * @throws Error if the target key is not registered on-chain
1948
+ * @throws Error if the fingerprint doesn't match the fingerprint of the primary key
1949
+ * @throws Error if the revocation signature is invalid or not made by the issuer
1950
+ * @throws Error if wallet client is not configured
1951
+ * @throws Error if transaction fails
1952
+ */
1953
+ async revokeCertification(issuer, keyWithRevokedCertification) {
1954
+ try {
1955
+ if (issuer.getFingerprint() === keyWithRevokedCertification.getFingerprint()) {
1956
+ throw new Web3PGPServiceValidationError("Issuer must be different from the target key.");
1957
+ }
1958
+ const now = /* @__PURE__ */ new Date();
1959
+ const pk = keyWithRevokedCertification;
1960
+ let hasValidRevocation = false;
1961
+ for (const user of pk.users) {
1962
+ console.debug(`[WEB3PGP SERVICE] Verifying ${user.revocationSignatures.length} revocation signatures for user ID "${user.userID?.toString()}"...`);
1963
+ const revokedCertifications = [];
1964
+ const validRevocations = [];
1965
+ for (const revSig of user.revocationSignatures) {
1966
+ try {
1967
+ const issuerKeyPacket = issuer.getKeys().find((k) => k.getKeyID().equals(revSig.issuerKeyID));
1968
+ if (!issuerKeyPacket) {
1969
+ throw new Web3PGPServiceValidationError(`Issuer key packet with Key ID ${revSig.issuerKeyID.toHex()} not found in issuer key.`);
1970
+ }
1971
+ console.debug(`[WEB3PGP SERVICE] Verifying revocation signature made by ${revSig.issuerFingerprint ? toHex(revSig.issuerFingerprint) : revSig.issuerKeyID.toHex()}...`);
1972
+ console.debug(`[WEB3PGP SERVICE] Valid revocation signature found.`);
1973
+ validRevocations.push(revSig);
1974
+ hasValidRevocation = true;
1975
+ } catch (e) {
1976
+ console.debug(`[WEB3PGP SERVICE] Invalid revocation signature found and skipped: ${e}`);
1977
+ continue;
1978
+ }
1979
+ }
1980
+ for (const cert of user.otherCertifications) {
1981
+ for (const revSig of validRevocations) {
1982
+ if (cert.issuerKeyID.equals(revSig.issuerKeyID)) {
1983
+ cert.revoked = cert.revoked || (cert.created || now) <= (revSig.created || now);
1984
+ if (cert.revoked) {
1985
+ revokedCertifications.push(cert);
1986
+ break;
1987
+ }
1988
+ }
1989
+ }
1990
+ }
1991
+ user.revocationSignatures = validRevocations;
1992
+ user.otherCertifications = revokedCertifications;
1993
+ }
1994
+ if (!hasValidRevocation) {
1995
+ throw new Web3PGPServiceValidationError("No valid certification revocation signature made by the issuer was found on the target key.");
1996
+ }
1997
+ return this.web3pgp.revokeCertification(
1998
+ toBytes32(to0x(pk.getFingerprint())),
1999
+ toBytes32(to0x(issuer.getFingerprint())),
2000
+ toHex(pk.toPublic().write())
2001
+ );
2002
+ } catch (err) {
2003
+ throw new Web3PGPServiceError(`Failed to revoke certification: ${err}`);
2004
+ }
2005
+ }
2006
+ /*****************************************************************************************************************/
2007
+ /* LOG VALIDATION AND EXTRACTION FUNCTIONS */
2008
+ /*****************************************************************************************************************/
2009
+ /**
2010
+ * Validate and extract the public key from a KeyRegisteredLog event.
2011
+ *
2012
+ * @description
2013
+ *
2014
+ * This method:
2015
+ * 1. Validates the log data contains required fields
2016
+ * 2. Extracts and parses the OpenPGP message from the log
2017
+ * 3. Verifies the primary key fingerprint matches the declared one
2018
+ * 4. (if verifications are enabled) Verifies the primary key has a valid signature, is not expired and is not
2019
+ * revoked at the time of registration (uses the block timestamp)..
2020
+ * 5. Validates all declared subkeys are present in the key
2021
+ * 6. (if verifications are enabled) Verifies each subkey has a valid signature, is not expired and is not
2022
+ * revoked at the time of registration (uses the block timestamp).
2023
+ * 7. Prunes any extra subkeys not declared in the log
2024
+ *
2025
+ * Cryptographic verifications of the keys (steps 4 and 6) can be skipped by setting the `skipCryptographicVerifications`
2026
+ * parameter to true. This is useful when users want to extract and parse the OpenPGP key material in order to perform
2027
+ * custom validations or inspections in case the verification fails, is expected to fail or is performed by an external
2028
+ * OpenPGP toolkit.
2029
+ *
2030
+ * @param log The KeyRegisteredLog event data from the blockchain
2031
+ * @param skipCryptographicVerifications If true, skips cryptographic verifications of key and subkeys. Defaults to false.
2032
+ * @returns The validated OpenPGP public key extracted from the log
2033
+ *
2034
+ * @throws Web3PGPServiceValidationError if the log data is invalid or missing required fields
2035
+ * @throws Web3PGPServiceValidationError if the extracted OpenPGP message is invalid or corrupted
2036
+ * @throws Web3PGPServiceValidationError if the primary key fingerprint does not match the log data
2037
+ * @throws Web3PGPServiceValidationError if any declared subkey is missing from the extracted key
2038
+ *
2039
+ * @example
2040
+ * ```typescript
2041
+ * const logs = await web3pgp.searchKeyRegisteredLogs();
2042
+ * for (const log of logs) {
2043
+ * try {
2044
+ * const publicKey = await service.extractFromKeyRegisteredLog(log);
2045
+ * console.log(`Valid key: ${publicKey.getFingerprint()}`);
2046
+ * } catch (err) {
2047
+ * console.warn(`Invalid log data: ${err.message}`);
2048
+ * }
2049
+ * }
2050
+ * ```
2051
+ */
2052
+ async extractFromKeyRegisteredLog(log, skipCryptographicVerifications) {
2053
+ try {
2054
+ if (!log.openPGPMsg || !log.primaryKeyFingerprint) {
2055
+ throw new Web3PGPServiceValidationError(`The KeyRegisteredLog event is missing the data needed to extract and validate the public key.`);
2056
+ }
2057
+ const primaryKey = await openpgp2.readKey({ binaryKey: toBytes(log.openPGPMsg) });
2058
+ if (skipCryptographicVerifications !== true) {
2059
+ await primaryKey.verifyPrimaryKey(log.blockTimestamp);
2060
+ }
2061
+ if (toBytes32(to0x(primaryKey.getFingerprint())) !== toBytes32(to0x(log.primaryKeyFingerprint))) {
2062
+ throw new Web3PGPServiceValidationError(`The fingerprint of the retrieved primary key does not match the declared fingerprint in the KeyRegisteredLog event.`);
2063
+ }
2064
+ let subkeys = [];
2065
+ for (const subkeyFingerprint of log.subkeyFingerprints || []) {
2066
+ const normalizedSubkeyFingerprint = toBytes32(to0x(subkeyFingerprint));
2067
+ const subkey = primaryKey.subkeys.find((sk) => toBytes32(to0x(sk.getFingerprint())) === normalizedSubkeyFingerprint);
2068
+ if (subkey) {
2069
+ if (skipCryptographicVerifications !== true) {
2070
+ await subkey.verify(log.blockTimestamp);
2071
+ }
2072
+ subkeys.push(subkey);
2073
+ } else {
2074
+ throw new Web3PGPServiceValidationError(`The primary key does not contain the declared subkey with fingerprint ${subkeyFingerprint} from the KeyRegisteredLog event.`);
2075
+ }
2076
+ }
2077
+ primaryKey.subkeys = subkeys;
2078
+ console.debug(`[Web3PGP - Service] Successfully extracted primary key ${log.primaryKeyFingerprint} with ${subkeys.length} subkeys from KeyRegisteredLog event`);
2079
+ return primaryKey.toPublic();
2080
+ } catch (err) {
2081
+ if (err instanceof Web3PGPServiceValidationError) {
2082
+ throw err;
2083
+ }
2084
+ throw new Web3PGPServiceValidationError(`Failed to read the OpenPGP message from the KeyRegisteredLog event: ${err}`);
2085
+ }
2086
+ }
2087
+ /**
2088
+ * Validate and extract the subkey from a SubkeyAddedLog event.
2089
+ *
2090
+ * @description
2091
+ * This method:
2092
+ * 1. Validates the log data contains required fields
2093
+ * 2. Extracts and parses the OpenPGP message from the log
2094
+ * 3. Verifies the primary key fingerprint matches the declared one
2095
+ * 4. Verifies the subkey fingerprint matches the declared one
2096
+ * 5. Prunes any extra subkeys and user ID packets, returning only the primary key and the added subkey
2097
+ * 6. (if verifications are enabled) Verifies the primary key has a valid signature, is not expired and is not
2098
+ * revoked at the time of subkey addition (uses the block timestamp).
2099
+ * 7. (if verifications are enabled) Verifies the subkey has a valid signature, is not expired and is not
2100
+ * revoked at the time of addition (uses the block timestamp).
2101
+ *
2102
+ * Cryptographic verifications of the keys (steps 6 and 7) can be skipped by setting the `skipCryptographicVerifications`
2103
+ * parameter to true. This is useful when users want to extract and parse the OpenPGP key material in order to perform
2104
+ * custom validations or inspections in case the verification fails, is expected to fail or is performed by an external
2105
+ * OpenPGP toolkit.
2106
+ *
2107
+ * @param log The SubkeyAddedLog event data from the blockchain
2108
+ * @param skipCryptographicVerifications If true, skips cryptographic verifications of key and subkey. Defaults to false.
2109
+ * @returns The validated OpenPGP public key containing the primary key and the added subkey
2110
+ *
2111
+ * @throws Web3PGPServiceValidationError if the log data is invalid or missing required fields
2112
+ * @throws Web3PGPServiceValidationError if the extracted OpenPGP message is invalid or corrupted
2113
+ * @throws Web3PGPServiceValidationError if the primary key fingerprint does not match the log data
2114
+ * @throws Web3PGPServiceValidationError if the subkey is missing from the extracted key
2115
+ *
2116
+ * @example
2117
+ * ```typescript
2118
+ * const logs = await web3pgp.searchSubkeyAddedLogs(primaryFingerprint);
2119
+ * let primaryKey = await service.getPublicKey(primaryFingerprint);
2120
+ * for (const log of logs) {
2121
+ * const subkey = await service.extractFromSubkeyAddedLog(log);
2122
+ * primaryKey = await primaryKey.update(subkey);
2123
+ * }
2124
+ * ```
2125
+ */
2126
+ async extractFromSubkeyAddedLog(log, skipCryptographicVerifications) {
2127
+ try {
2128
+ let pk;
2129
+ try {
2130
+ pk = await openpgp2.readKey({ binaryKey: toBytes(log.openPGPMsg) });
2131
+ pk = await OpenPGPUtils.sanitizeSubkey(pk, log.subkeyFingerprint);
2132
+ if (skipCryptographicVerifications !== true) {
2133
+ await OpenPGPUtils.verifyKey(pk, log.blockTimestamp);
2134
+ }
2135
+ } catch (err) {
2136
+ throw new Web3PGPServiceValidationError(`Failed to read and sanitize the OpenPGP message for subkey with fingerprint ${log.subkeyFingerprint} from SubkeyAddedLog event: ${err}`);
2137
+ }
2138
+ if (toBytes32(to0x(pk.getFingerprint())) !== toBytes32(to0x(log.primaryKeyFingerprint))) {
2139
+ throw new Web3PGPServiceValidationError(`The primary key fingerprint ${pk.getFingerprint()} does not match the declared primary key fingerprint ${log.primaryKeyFingerprint} in SubkeyAddedLog event.`);
2140
+ }
2141
+ return pk.toPublic();
2142
+ } catch (err) {
2143
+ if (err instanceof Web3PGPServiceValidationError) {
2144
+ throw err;
2145
+ }
2146
+ throw new Web3PGPServiceValidationError(`Failed to read the OpenPGP message from the SubkeyAddedLog event: ${err}`);
2147
+ }
2148
+ }
2149
+ /**
2150
+ * Validate and extract the revoked key or revocation certificate from a KeyRevokedLog event.
2151
+ *
2152
+ * @description
2153
+ * This method handles two types of revocation data:
2154
+ * 1. Key certificates: Full OpenPGP keys with revocation signatures
2155
+ * 2. Standalone revocation certificates: Revocation signature packets only
2156
+ *
2157
+ * The method:
2158
+ * 1. Validates the log data contains required fields
2159
+ * 2. Attempts to parse as a key certificate first
2160
+ * 3. If that fails, attempts to parse as a standalone revocation certificate and returns the armored certificate.
2161
+ * It is the user's responsibility to verify and apply the revocation certificate to the target key.
2162
+ * 4. For key certificates, validates the fingerprint matches the target key
2163
+ * 5. (if verifications are enabled) Verifies the primary key has a valid signature, is not expired and is revoked
2164
+ * at the time of revocation (uses the block timestamp).
2165
+ * 6. Returns either the revoked key or the armored revocation certificate
2166
+ *
2167
+ * @param log The KeyRevokedLog event data from the blockchain
2168
+ * @param skipCryptographicVerifications If true, skips cryptographic verifications of the revoked key. Defaults to false.
2169
+ * @returns A tuple containing either:
2170
+ * - [revokedKey, undefined] if a valid key certificate was found
2171
+ * - [undefined, armoredCert] if a standalone revocation certificate was found
2172
+ *
2173
+ * @throws Web3PGPServiceValidationError if the log data is invalid or missing required fields
2174
+ * @throws Web3PGPServiceValidationError if the extracted OpenPGP message is invalid or corrupted
2175
+ * @throws Web3PGPServiceValidationError if a key certificate does not effectively revoke the target key
2176
+ *
2177
+ * @example
2178
+ * ```typescript
2179
+ * const logs = await web3pgp.searchKeyRevokedLogs(fingerprint);
2180
+ * let publicKey = await service.getPublicKey(fingerprint);
2181
+ * for (const log of logs) {
2182
+ * const [revokedKey, revocationCert] = await service.extractFromKeyRevokedLog(log);
2183
+ * if (revokedKey) {
2184
+ * publicKey = await publicKey.update(revokedKey);
2185
+ * } else if (revocationCert) {
2186
+ * const result = await openpgp.revokeKey({
2187
+ * key: publicKey,
2188
+ * revocationCertificate: revocationCert
2189
+ * });
2190
+ * publicKey = result.publicKey;
2191
+ * }
2192
+ * }
2193
+ * ```
2194
+ */
2195
+ async extractFromKeyRevokedLog(log, skipCryptographicVerifications) {
2196
+ try {
2197
+ if (!log.fingerprint || !log.revocationCertificate) {
2198
+ throw new Web3PGPServiceValidationError(`The KeyRevokedLog event is missing the data needed to extract and validate the revocation.`);
2199
+ }
2200
+ let revokedKey;
2201
+ try {
2202
+ revokedKey = await openpgp2.readKey({ binaryKey: toBytes(log.revocationCertificate) });
2203
+ } catch (err) {
2204
+ const cert = await openpgp2.readMessage({
2205
+ binaryMessage: toBytes(log.revocationCertificate),
2206
+ config: {
2207
+ // Standalone revocation certs do not have public keys needed to verify the revocations signature
2208
+ // This causes OpenPGP.js to throw an error unless we allow unauthenticated messages
2209
+ allowMissingKeyFlags: true,
2210
+ allowUnauthenticatedMessages: true
2211
+ }
2212
+ });
2213
+ console.debug(`[Web3PGP - Service] Successfully extracted revocation certificate for key ${log.fingerprint} from KeyRevokedLog event`);
2214
+ return [void 0, cert.armor()];
2215
+ }
2216
+ if (toBytes32(to0x(revokedKey.getFingerprint())) === toBytes32(to0x(log.fingerprint))) {
2217
+ const pk = await OpenPGPUtils.sanitizePrimaryKey(revokedKey);
2218
+ if (skipCryptographicVerifications !== true && !await pk.isRevoked(void 0, void 0, log.blockTimestamp)) {
2219
+ throw new Web3PGPServiceValidationError(`The primary key with fingerprint ${pk.getFingerprint()} is not revoked as expected in the KeyRevokedLog event.`);
2220
+ }
2221
+ console.debug(`[Web3PGP - Service] Successfully extracted revocation for primary key ${log.fingerprint} from KeyRevokedLog event`);
2222
+ return [pk.toPublic(), void 0];
2223
+ } else {
2224
+ const pk = await OpenPGPUtils.sanitizeSubkey(revokedKey, log.fingerprint);
2225
+ if (skipCryptographicVerifications !== true && !await OpenPGPUtils.isSubkeyRevoked(pk.subkeys[0], pk, log.blockTimestamp)) {
2226
+ throw new Web3PGPServiceValidationError(`The subkey with fingerprint ${log.fingerprint} is not revoked as expected in the KeyRevokedLog event.`);
2227
+ }
2228
+ console.debug(`[Web3PGP - Service] Successfully extracted revocation for subkey ${log.fingerprint} from KeyRevokedLog event`);
2229
+ return [pk.toPublic(), void 0];
2230
+ }
2231
+ } catch (err) {
2232
+ if (err instanceof Web3PGPServiceValidationError) {
2233
+ throw err;
2234
+ }
2235
+ throw new Web3PGPServiceValidationError(`Failed to read the OpenPGP message from the KeyRevokedLog event: ${err}`);
2236
+ }
2237
+ }
2238
+ /**
2239
+ * Validate and extract the updated public key from a KeyUpdatedLog event.
2240
+ *
2241
+ * @description
2242
+ * This method:
2243
+ * 1. Validates the log data contains required fields
2244
+ * 2. Extracts and parses the OpenPGP message from the log
2245
+ * 3. Verifies the primary key fingerprint matches the declared one
2246
+ * 4. (if verifications are enabled) Verifies the primary key has a valid signature, is not expired and is not
2247
+ * revoked at the time of update (uses the block timestamp).
2248
+ *
2249
+ * Cryptographic verifications of the key (step 4) can be skipped by setting the `skipCryptographicVerifications`
2250
+ * parameter to true. This is useful when users want to extract and parse the OpenPGP key material in order to perform
2251
+ * custom validations or inspections in case the verification fails, is expected to fail or is performed by an external
2252
+ * OpenPGP toolkit.
2253
+ *
2254
+ * @param log The KeyUpdatedLog event data from the blockchain
2255
+ * @param skipCryptographicVerifications If true, skips cryptographic verifications of key. Defaults to false.
2256
+ * @returns The validated OpenPGP public key extracted from the log
2257
+ *
2258
+ * @throws Web3PGPServiceValidationError if the log data is invalid or missing required fields
2259
+ * @throws Web3PGPServiceValidationError if the extracted OpenPGP message is invalid or corrupted
2260
+ * @throws Web3PGPServiceValidationError if the primary key fingerprint does not match the log data
2261
+ *
2262
+ * @example
2263
+ * ```typescript
2264
+ * const logs = await web3pgp.searchKeyUpdatedLogs();
2265
+ * for (const log of logs) {
2266
+ * try {
2267
+ * const publicKey = await service.extractFromKeyUpdatedLog(log);
2268
+ * console.log(`Valid updated key: ${publicKey.getFingerprint()}`);
2269
+ * } catch (err) {
2270
+ * console.warn(`Invalid log data: ${err.message}`);
2271
+ * }
2272
+ * }
2273
+ * ```
2274
+ */
2275
+ async extractFromKeyUpdatedLog(log, skipCryptographicVerifications) {
2276
+ try {
2277
+ let pk = await openpgp2.readKey({ binaryKey: toBytes(log.openPGPMsg) });
2278
+ const pkFp = toBytes32(to0x(pk.getFingerprint()));
2279
+ if (pkFp !== toBytes32(to0x(log.fingerprint))) {
2280
+ throw new Web3PGPServiceValidationError(`The fingerprint of the retrieved primary key ${pkFp} does not match the declared fingerprint in the KeyUpdatedLog event ${log.fingerprint}`);
2281
+ }
2282
+ pk = await OpenPGPUtils.sanitizePrimaryKey(pk);
2283
+ if (skipCryptographicVerifications !== true) {
2284
+ await OpenPGPUtils.verifyKey(pk, log.blockTimestamp);
2285
+ }
2286
+ console.debug(`[Web3PGP - Service] Successfully extracted updated key ${pk.getFingerprint()} from KeyUpdatedLog event`);
2287
+ return pk.toPublic();
2288
+ } catch (err) {
2289
+ if (err instanceof Web3PGPServiceValidationError) {
2290
+ throw err;
2291
+ }
2292
+ throw new Web3PGPServiceValidationError(`Failed to extract the public key form the OpenPGP message in the KeyUpdatedLog event: ${err}`);
2293
+ }
2294
+ }
2295
+ /**
2296
+ * Validate and extract the certified public key from a KeyCertifiedLog event.
2297
+ *
2298
+ * @description
2299
+ * This method:
2300
+ * 1. Validates the log data contains required fields
2301
+ * 2. Extracts and parses the OpenPGP message from the log
2302
+ * 3. Verifies the primary key fingerprint matches the declared one
2303
+ * 4. (if verifications are enabled) Verifies the primary key has a valid signature, is not expired and is not
2304
+ * revoked at the time of certification (uses the block timestamp).
2305
+ * 5. Validates that at least one certification signature made by the issuer over the target key is present
2306
+ *
2307
+ * Cryptographic verifications of the key (step 4) can be skipped by setting the `skipCryptographicVerifications`
2308
+ * parameter to true. This is useful when users want to extract and parse the OpenPGP key material in order to perform
2309
+ * custom validations or inspections in case the verification fails, is expected to fail or is performed by an external
2310
+ * OpenPGP toolkit.
2311
+ *
2312
+ * @param log The KeyCertifiedLog event data from the blockchain
2313
+ * @param skipCryptographicVerifications If true, skips cryptographic verifications of key. Defaults to false.
2314
+ * @returns The validated OpenPGP public key extracted from the log
2315
+ *
2316
+ * @throws Web3PGPServiceValidationError if the log data is invalid or missing required fields
2317
+ * @throws Web3PGPServiceValidationError if the extracted OpenPGP message is invalid or corrupted
2318
+ * @throws Web3PGPServiceValidationError if the primary key fingerprint does not match the log data
2319
+ * @throws Web3PGPServiceValidationError if no valid certification signature made by the issuer is found
2320
+ *
2321
+ * @example
2322
+ * ```typescript
2323
+ * const logs = await web3pgp.searchKeyCertifiedLogs(issuerFingerprint, targetFingerprint);
2324
+ * for (const log of logs) {
2325
+ * try {
2326
+ * const certifiedKey = await service.extractFromKeyCertifiedLog(log);
2327
+ * console.log(`Valid certified key: ${certifiedKey.getFingerprint()}`);
2328
+ * } catch (err) {
2329
+ * console.warn(`Invalid log data: ${err.message}`);
2330
+ * }
2331
+ * }
2332
+ * ```
2333
+ */
2334
+ async extractFromKeyCertifiedLog(log, skipCryptographicVerifications) {
2335
+ try {
2336
+ let pk = await openpgp2.readKey({ binaryKey: toBytes(log.keyCertificate) });
2337
+ const pkFp = toBytes32(to0x(pk.getFingerprint()));
2338
+ if (pkFp !== toBytes32(to0x(log.fingerprint))) {
2339
+ throw new Web3PGPServiceValidationError(`The fingerprint of the retrieved primary key ${pkFp} does not match the declared fingerprint in the KeyCertifiedLog event ${log.fingerprint}`);
2340
+ }
2341
+ pk = await OpenPGPUtils.sanitizePrimaryKey(pk);
2342
+ pk.users.forEach((user) => {
2343
+ user.revocationSignatures = [];
2344
+ });
2345
+ if (skipCryptographicVerifications !== true) {
2346
+ await OpenPGPUtils.verifyKey(pk, log.blockTimestamp);
2347
+ }
2348
+ console.debug(`[Web3PGP - Service] Successfully extracted updated key ${pk.getFingerprint()} from KeyUpdatedLog event`);
2349
+ return pk.toPublic();
2350
+ } catch (err) {
2351
+ if (err instanceof Web3PGPServiceValidationError) {
2352
+ throw err;
2353
+ }
2354
+ throw new Web3PGPServiceValidationError(`Failed to extract the public key form the OpenPGP message in the KeyUpdatedLog event: ${err}`);
2355
+ }
2356
+ }
2357
+ /**
2358
+ * Validate and extract the revoked certification from a KeyCertificationRevokedLog event.
2359
+ *
2360
+ * @description
2361
+ * This method:
2362
+ * 1. Validates the log data contains required fields
2363
+ * 2. Extracts and parses the OpenPGP message from the log
2364
+ * 3. Verifies the primary key fingerprint matches the declared one
2365
+ * 4. (if verifications are enabled) Verifies the primary key has a valid signature, is not expired and is not
2366
+ * revoked at the time of certification revocation (uses the block timestamp).
2367
+ * 5. Validates that at least one certification revocation signature made by the issuer over the target key is present
2368
+ *
2369
+ * Cryptographic verifications of the key (step 4) can be skipped by setting the `skipCryptographicVerifications`
2370
+ * parameter to true. This is useful when users want to extract and parse the OpenPGP key material in order to perform
2371
+ * custom validations or inspections in case the verification fails, is expected to fail or is performed by an external
2372
+ * OpenPGP toolkit.
2373
+ *
2374
+ * @param log The KeyCertificationRevokedLog event data from the blockchain
2375
+ * @param skipCryptographicVerifications If true, skips cryptographic verifications of key. Defaults to false.
2376
+ * @returns The validated OpenPGP public key extracted from the log
2377
+ *
2378
+ * @throws Web3PGPServiceValidationError if the log data is invalid or missing required fields
2379
+ * @throws Web3PGPServiceValidationError if the extracted OpenPGP message is invalid or corrupted
2380
+ * @throws Web3PGPServiceValidationError if the primary key fingerprint does not match the log data
2381
+ * @throws Web3PGPServiceValidationError if no valid certification revocation signature made by the issuer is found
2382
+ *
2383
+ * @example
2384
+ * ```typescript
2385
+ * const logs = await web3pgp.searchKeyCertificationRevokedLogs(issuerFingerprint, targetFingerprint);
2386
+ * for (const log of logs) {
2387
+ * try {
2388
+ * const revokedCertificationKey = await service.extractFromKeyCertificationRevokedLog(log);
2389
+ * console.log(`Valid revoked certification key: ${revokedCertificationKey.getFingerprint()}`);
2390
+ * } catch (err) {
2391
+ * console.warn(`Invalid log data: ${err.message}`);
2392
+ * }
2393
+ * }
2394
+ * ```
2395
+ */
2396
+ async extractFromKeyCertificationRevokedLog(log, skipCryptographicVerifications) {
2397
+ try {
2398
+ let pk = await openpgp2.readKey({ binaryKey: toBytes(log.revocationSignature) });
2399
+ const pkFp = toBytes32(to0x(pk.getFingerprint()));
2400
+ const issuerFp = toBytes32(to0x(log.issuerFingerprint));
2401
+ if (pkFp !== toBytes32(to0x(log.fingerprint))) {
2402
+ throw new Web3PGPServiceValidationError(`The fingerprint of the retrieved primary key ${pkFp} does not match the declared fingerprint in the KeyCertificationRevokedLog event ${log.fingerprint}`);
2403
+ }
2404
+ pk = await OpenPGPUtils.sanitizePrimaryKey(pk);
2405
+ if (skipCryptographicVerifications !== true) {
2406
+ await OpenPGPUtils.verifyKey(pk, log.blockTimestamp);
2407
+ }
2408
+ console.debug(`[Web3PGP - Service] Successfully extracted updated key ${pk.getFingerprint()} from KeyUpdatedLog event`);
2409
+ return pk.toPublic();
2410
+ } catch (err) {
2411
+ if (err instanceof Web3PGPServiceValidationError) {
2412
+ throw err;
2413
+ }
2414
+ throw new Web3PGPServiceValidationError(`Failed to extract the public key form the OpenPGP message in the KeyUpdatedLog event: ${err}`);
2415
+ }
2416
+ }
2417
+ /**
2418
+ * Extract the signature from an OwnershipChallengedLog event. The signature is not verified at this stage.
2419
+ *
2420
+ * @description
2421
+ * This method:
2422
+ * 1. Validates the log data contains required fields
2423
+ * 2. Extracts and parses the OpenPGP signature from the log
2424
+ *
2425
+ * @param log The OwnershipProvedLog event data from the blockchain
2426
+ * @returns The OpenPGP signature extracted from the log
2427
+ *
2428
+ * @throws Web3PGPServiceValidationError if the log data is invalid or missing required fields
2429
+ * @throws Web3PGPServiceValidationError if the extracted OpenPGP signature is invalid or corrupted
2430
+ */
2431
+ async extractFromOwnershipProvedLog(log) {
2432
+ try {
2433
+ return openpgp2.readSignature({ binarySignature: toBytes(log.signature) });
2434
+ } catch (err) {
2435
+ throw new Web3PGPServiceValidationError(`Failed to read the OpenPGP signature from the OwnershipProvedLog event: ${err}`);
2436
+ }
2437
+ }
2438
+ /*****************************************************************************************************************/
2439
+ /* KEY RETRIEVAL */
2440
+ /*****************************************************************************************************************/
2441
+ /**
2442
+ * Retrieve and reconstruct an OpenPGP public key from the blockchain by its fingerprint.
2443
+ *
2444
+ * @param fingerprint The fingerprint of the key to retrieve (primary key or subkey)
2445
+ * @returns The reconstructed and validated OpenPGP public key, with revocations applied if any
2446
+ *
2447
+ * @throws Error if the key is not registered on-chain
2448
+ * @throws Error if the key data cannot be retrieved from blockchain events
2449
+ * @throws Error if the retrieved OpenPGP message is invalid or corrupted
2450
+ */
2451
+ async getPublicKey(fingerprint) {
2452
+ const normalizedFingerprint = toBytes32(to0x(fingerprint));
2453
+ console.debug(`[Web3PGP - Service] Retrieving public key for fingerprint: ${normalizedFingerprint}`);
2454
+ const [publicationBlock, parent] = await Promise.allSettled([
2455
+ this.web3pgp.getKeyPublicationBlock(normalizedFingerprint),
2456
+ this.web3pgp.parentOf(normalizedFingerprint)
2457
+ ]);
2458
+ if (publicationBlock.status === "rejected") {
2459
+ throw new Web3PGPServiceCriticalError(`Failed to retrieve key publication block: ${publicationBlock.reason}`, publicationBlock.reason);
2460
+ }
2461
+ if (parent.status === "rejected") {
2462
+ throw new Web3PGPServiceCriticalError(`Failed to retrieve parent fingerprint: ${parent.reason}`, parent.reason);
2463
+ }
2464
+ const publicationBlockNumber = publicationBlock.value;
2465
+ const parentFingerprint = parent.value;
2466
+ if (publicationBlockNumber === BigInt(0)) {
2467
+ console.debug(`[Web3PGP - Service] Key ${normalizedFingerprint} is not registered on-chain`);
2468
+ throw new Web3PGPServiceError(`The key with fingerprint ${fingerprint} is not registered on-chain.`);
2469
+ }
2470
+ console.debug(`[Web3PGP - Service] Key ${normalizedFingerprint} published at block ${publicationBlockNumber}, parent: ${parentFingerprint}`);
2471
+ if (parentFingerprint !== BYTES32_ZERO) {
2472
+ console.debug(`[Web3PGP - Service] Key ${normalizedFingerprint} is a subkey, retrieving parent key to reconstruct full key`);
2473
+ return await this.getPublicKey(parentFingerprint);
2474
+ }
2475
+ const subkeys = await this.fetchAllPaginated((start, limit) => this.web3pgp.listSubkeys(normalizedFingerprint, start, limit));
2476
+ let blockNumbers = await Promise.all([
2477
+ this.web3pgp.getKeyPublicationBlockBatch(subkeys),
2478
+ this.fetchAllPaginated((start, limit) => this.web3pgp.listKeyUpdates(normalizedFingerprint, start, limit)),
2479
+ this.fetchAllPaginated((start, limit) => this.web3pgp.listRevocations(normalizedFingerprint, start, limit)),
2480
+ this.fetchAllPaginated((start, limit) => this.web3pgp.listCertifications(normalizedFingerprint, start, limit)),
2481
+ this.fetchAllPaginated((start, limit) => this.web3pgp.listCertificationRevocations(normalizedFingerprint, start, limit)),
2482
+ ...subkeys.map(
2483
+ (subkeyFingerprint) => this.concurrencyLimit(
2484
+ () => this.fetchAllPaginated(
2485
+ (start, limit) => this.web3pgp.listRevocations(subkeyFingerprint, start, limit)
2486
+ )
2487
+ )
2488
+ )
2489
+ ]);
2490
+ const rawBlocks = [publicationBlockNumber, ...blockNumbers.flat()];
2491
+ const uniqueBlocks = [...new Set(rawBlocks)].filter((b) => b > 0n);
2492
+ const expectationsMap = /* @__PURE__ */ new Map();
2493
+ for (const blockNumber of uniqueBlocks) {
2494
+ const isPublicationBlock = blockNumber === publicationBlockNumber;
2495
+ const subkeyAddedEventsExpected = blockNumbers[0].filter((b) => b === blockNumber && b !== publicationBlockNumber).length;
2496
+ const totalRegistrationEventsExpected = subkeyAddedEventsExpected + (isPublicationBlock ? 1 : 0);
2497
+ const KeyUpdatedExpected = blockNumbers[1].filter((b) => b === blockNumber).length;
2498
+ let KeyRevokedExpected = blockNumbers[2].filter((b) => b === blockNumber).length;
2499
+ const KeyCertifiedExpected = blockNumbers[3].filter((b) => b === blockNumber).length;
2500
+ const KeyCertificationRevokedExpected = blockNumbers[4].filter((b) => b === blockNumber).length;
2501
+ for (let i = 5; i < blockNumbers.length; i++) {
2502
+ KeyRevokedExpected += blockNumbers[i].filter((b) => b === blockNumber).length;
2503
+ }
2504
+ expectationsMap.set(blockNumber, (logs2) => {
2505
+ const counts = {
2506
+ [Web3PGPEvents.KeyRegistered]: 0,
2507
+ [Web3PGPEvents.SubkeyAdded]: 0,
2508
+ [Web3PGPEvents.KeyUpdated]: 0,
2509
+ [Web3PGPEvents.KeyRevoked]: 0,
2510
+ [Web3PGPEvents.KeyCertified]: 0,
2511
+ [Web3PGPEvents.KeyCertificationRevoked]: 0
2512
+ };
2513
+ for (const log of logs2) {
2514
+ if (counts.hasOwnProperty(log.type)) {
2515
+ counts[log.type]++;
2516
+ }
2517
+ }
2518
+ const keyRegisteredOrSubkeyAddedCount = counts[Web3PGPEvents.KeyRegistered] + counts[Web3PGPEvents.SubkeyAdded];
2519
+ return keyRegisteredOrSubkeyAddedCount === totalRegistrationEventsExpected && counts[Web3PGPEvents.KeyUpdated] === KeyUpdatedExpected && counts[Web3PGPEvents.KeyRevoked] === KeyRevokedExpected && counts[Web3PGPEvents.KeyCertified] === KeyCertifiedExpected && counts[Web3PGPEvents.KeyCertificationRevoked] === KeyCertificationRevokedExpected;
2520
+ });
2521
+ }
2522
+ const logs = await Promise.all(
2523
+ uniqueBlocks.map(
2524
+ (blockNumber) => this.concurrencyLimit(async () => {
2525
+ for (let attempt = 1; attempt <= 3; attempt++) {
2526
+ console.debug(`[Web3PGP - Service] Searching for key events for block ${blockNumber}, attempt ${attempt}...`);
2527
+ let logs2 = await this.web3pgp.searchKeyEvents([normalizedFingerprint, ...subkeys], blockNumber, blockNumber);
2528
+ const validate = expectationsMap.get(blockNumber);
2529
+ if (!validate) {
2530
+ throw new Web3PGPServiceCriticalError(`Missing expectations validator for block ${blockNumber}`);
2531
+ }
2532
+ if (validate(logs2)) {
2533
+ console.debug(`[Web3PGP - Service] Retrieved all (${logs2.length}) expected logs for block ${blockNumber} after ${attempt} attempt(s)`);
2534
+ return logs2;
2535
+ } else {
2536
+ const delay = 200 * Math.pow(2, attempt - 1);
2537
+ console.debug(`[Web3PGP - Service] Retrieved logs for block ${blockNumber} do not match expectations on attempt ${attempt}. Retrying in ${delay}ms...`);
2538
+ await new Promise((resolve) => setTimeout(resolve, delay));
2539
+ }
2540
+ }
2541
+ throw new Web3PGPServiceError(`Failed to retrieve expected logs for block ${blockNumber} after 3 attempts.`);
2542
+ })
2543
+ )
2544
+ );
2545
+ const flattenedLogs = logs.flat().sort((a, b) => {
2546
+ if (a.blockNumber < b.blockNumber) return -1;
2547
+ if (a.blockNumber > b.blockNumber) return 1;
2548
+ if (a.logIndex < b.logIndex) return -1;
2549
+ if (a.logIndex > b.logIndex) return 1;
2550
+ return 0;
2551
+ });
2552
+ if (flattenedLogs[0].type !== Web3PGPEvents.KeyRegistered) {
2553
+ throw new Web3PGPServiceError(
2554
+ `Invalid event sequence: Key history must start with KeyRegistered. Found ${flattenedLogs[0].type} at block ${flattenedLogs[0].blockNumber} - tx ${flattenedLogs[0].transactionHash}.`
2555
+ );
2556
+ }
2557
+ console.debug(`[Web3PGP - Service] Found KeyRegistered log for primary key ${normalizedFingerprint} at block ${flattenedLogs[0].blockNumber} - tx ${flattenedLogs[0].transactionHash}`);
2558
+ let primaryKey = await this.extractFromKeyRegisteredLog(flattenedLogs[0]);
2559
+ console.debug(`[Web3PGP - Service] Reconstructing primary key ${normalizedFingerprint} from ${flattenedLogs.length} logs`);
2560
+ const historyLogs = flattenedLogs.slice(1);
2561
+ for (const log of historyLogs) {
2562
+ try {
2563
+ switch (log.type) {
2564
+ case Web3PGPEvents.SubkeyAdded:
2565
+ console.debug(`[Web3PGP - Service] Processing SubkeyAdded log for subkey ${log.subkeyFingerprint} at block ${log.blockNumber} - tx ${log.transactionHash}`);
2566
+ const subkey = await this.extractFromSubkeyAddedLog(log);
2567
+ primaryKey = await primaryKey.update(subkey, log.blockTimestamp);
2568
+ break;
2569
+ case Web3PGPEvents.KeyRevoked:
2570
+ console.debug(`[Web3PGP - Service] Processing KeyRevoked log for fingerprint ${log.fingerprint} at block ${log.blockNumber} - tx ${log.transactionHash}`);
2571
+ const [revokedKey, revocationCert] = await this.extractFromKeyRevokedLog(log);
2572
+ if (revokedKey) {
2573
+ primaryKey = await primaryKey.update(revokedKey, log.blockTimestamp);
2574
+ } else if (revocationCert) {
2575
+ try {
2576
+ const result = await openpgp2.revokeKey({
2577
+ key: primaryKey,
2578
+ revocationCertificate: revocationCert,
2579
+ date: log.blockTimestamp,
2580
+ format: "object"
2581
+ });
2582
+ primaryKey = await primaryKey.update(result.publicKey, log.blockTimestamp);
2583
+ } catch (err) {
2584
+ throw new Web3PGPServiceValidationError(`Failed to apply standalone revocation certificate for key ${log.fingerprint} from KeyRevokedLog event: ${err}`);
2585
+ }
2586
+ }
2587
+ break;
2588
+ case Web3PGPEvents.KeyUpdated:
2589
+ console.debug(`[Web3PGP - Service] Processing KeyUpdated log at block ${log.blockNumber} - tx ${log.transactionHash}`);
2590
+ const updatedKey = await this.extractFromKeyUpdatedLog(log);
2591
+ primaryKey = await primaryKey.update(updatedKey, log.blockTimestamp);
2592
+ break;
2593
+ case Web3PGPEvents.KeyCertified:
2594
+ console.debug(`[Web3PGP - Service] Processing KeyCertified log at block ${log.blockNumber} - tx ${log.transactionHash}`);
2595
+ const certifiedKey = await this.extractFromKeyCertifiedLog(log);
2596
+ primaryKey = await primaryKey.update(certifiedKey, log.blockTimestamp);
2597
+ break;
2598
+ case Web3PGPEvents.KeyCertificationRevoked:
2599
+ console.debug(`[Web3PGP - Service] Processing KeyCertificationRevoked log at block ${log.blockNumber} - tx ${log.transactionHash}`);
2600
+ const certRevokedKey = await this.extractFromKeyCertificationRevokedLog(log);
2601
+ primaryKey = await primaryKey.update(certRevokedKey, log.blockTimestamp);
2602
+ break;
2603
+ default:
2604
+ console.warn(`[Web3PGP - Service] Unused log type ${log.type} at block ${log.blockNumber} - tx ${log.transactionHash}`);
2605
+ break;
2606
+ }
2607
+ } catch (err) {
2608
+ if (err instanceof Web3PGPServiceValidationError) {
2609
+ console.warn(`[Web3PGP - Service] Failed to process log of type ${log.type} at block ${log.blockNumber} for key ${primaryKey.getFingerprint()}: ${err}`);
2610
+ } else {
2611
+ throw err;
2612
+ }
2613
+ }
2614
+ }
2615
+ console.debug(`[Web3PGP - Service] Successfully retrieved and reconstructed the public key`);
2616
+ return primaryKey;
2617
+ }
2618
+ /**
2619
+ * Searches for all key-related events within a specified block range. Optionally filters by fingerprints.
2620
+ *
2621
+ * Note: The fingerprints are the subjects of the events (i.e., the keys being registered, updated, revoked, certified, etc.).
2622
+ * Results will also include subkeys added, challenges, and proofs of ownership related to the listed fingerprints.
2623
+ *
2624
+ * @param fingerprints The fingerprint(s) of the keys to filter events for. Can be a single fingerprint or an array. Defaults to all keys if not provided.
2625
+ * @param fromBlock Starting block number (inclusive). Defaults to 'earliest' if not provided. 'pending' is not allowed.
2626
+ * @param toBlock Ending block number (inclusive). Defaults to 'latest' if not provided. 'pending' is not allowed.
2627
+ * @return An array of Web3PGPEventLog.
2628
+ */
2629
+ async searchKeyEvents(fingerprints, fromBlock, toBlock) {
2630
+ return this.web3pgp.searchKeyEvents(fingerprints, fromBlock, toBlock);
2631
+ }
2632
+ /**
2633
+ * Get the current block number of the connected blockchain.
2634
+ * @return The current block number as a bigint.
2635
+ */
2636
+ async getBlockNumber() {
2637
+ return this.web3pgp.getBlockNumber();
2638
+ }
2639
+ /*****************************************************************************************************************/
2640
+ /* UTILITY FUNCTIONS */
2641
+ /*****************************************************************************************************************/
2642
+ /**
2643
+ * Helper method to fetch all items from a paginated contract method.
2644
+ * @param fetchFn The paginated fetch function to call
2645
+ * @param limit The number of items to fetch per page
2646
+ * @param maxItems The maximum number of items to fetch in total (safety limit)
2647
+ * @returns An array containing all fetched items
2648
+ */
2649
+ async fetchAllPaginated(fetchFn, limit = 1000n, maxItems = 100000n) {
2650
+ const results = [];
2651
+ let start = 0n;
2652
+ do {
2653
+ const batch = await fetchFn(start, limit);
2654
+ results.push(...batch);
2655
+ if (batch.length < Number(limit) || results.length >= Number(maxItems)) {
2656
+ break;
2657
+ }
2658
+ start += limit;
2659
+ } while (true);
2660
+ return results;
2661
+ }
2662
+ };
2663
+
2664
+ // src/web3doc/types/types.ts
2665
+ var EventType = /* @__PURE__ */ ((EventType2) => {
2666
+ EventType2[EventType2["DOCUMENT"] = 0] = "DOCUMENT";
2667
+ EventType2[EventType2["COPY"] = 1] = "COPY";
2668
+ return EventType2;
2669
+ })(EventType || {});
2670
+ var Web3DocEvents = {
2671
+ Document: "Document",
2672
+ Copy: "Copy",
2673
+ Notification: "Notification",
2674
+ Signature: "Signature",
2675
+ Timestamp: "Timestamp",
2676
+ SignatureRevocation: "SignatureRevocation"
2677
+ };
2678
+
2679
+ // src/abis/Web3Doc.ts
2680
+ var Web3Doc = [{ "type": "constructor", "inputs": [], "stateMutability": "nonpayable" }, { "type": "receive", "stateMutability": "payable" }, { "type": "function", "name": "UPGRADE_INTERFACE_VERSION", "inputs": [], "outputs": [{ "name": "", "type": "string", "internalType": "string" }], "stateMutability": "view" }, { "type": "function", "name": "authority", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "copyOffChain", "inputs": [{ "name": "original", "type": "uint256", "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "internalType": "bytes32" }, { "name": "recipients", "type": "tuple[]", "internalType": "struct IWeb3Doc.Recipient[]", "components": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "signatureRequested", "type": "bool", "internalType": "bool" }] }, { "name": "uri", "type": "string", "internalType": "string" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "copyOnChain", "inputs": [{ "name": "original", "type": "uint256", "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "internalType": "bytes32" }, { "name": "recipients", "type": "tuple[]", "internalType": "struct IWeb3Doc.Recipient[]", "components": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "signatureRequested", "type": "bool", "internalType": "bool" }] }, { "name": "document", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "getDocumentBlockNumberByID", "inputs": [{ "name": "id", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "getDocumentBlockNumberByIDBatch", "inputs": [{ "name": "ids", "type": "uint256[]", "internalType": "uint256[]" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "getSignatureBlockNumberByHash", "inputs": [{ "name": "signatureHash", "type": "bytes32", "internalType": "bytes32" }], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "getSignatureBlockNumberByHashBatch", "inputs": [{ "name": "signatureHashes", "type": "bytes32[]", "internalType": "bytes32[]" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "getWeb3PGPAddress", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "initialize", "inputs": [{ "name": "fee", "type": "uint256", "internalType": "uint256" }, { "name": "manager", "type": "address", "internalType": "address" }, { "name": "web3pgp", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "initializeUpgrade", "inputs": [], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "isConsumingScheduledOp", "inputs": [], "outputs": [{ "name": "", "type": "bytes4", "internalType": "bytes4" }], "stateMutability": "view" }, { "type": "function", "name": "isCopyOf", "inputs": [{ "name": "id", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "listDocumentIdsByHash", "inputs": [{ "name": "dochash", "type": "bytes32", "internalType": "bytes32" }, { "name": "start", "type": "uint256", "internalType": "uint256" }, { "name": "limit", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "listSignatureRevocationsBlockNumbers", "inputs": [{ "name": "signatureHash", "type": "bytes32", "internalType": "bytes32" }, { "name": "start", "type": "uint256", "internalType": "uint256" }, { "name": "limit", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "listSignatures", "inputs": [{ "name": "id", "type": "uint256", "internalType": "uint256" }, { "name": "start", "type": "uint256", "internalType": "uint256" }, { "name": "limit", "type": "uint256", "internalType": "uint256" }], "outputs": [{ "name": "", "type": "uint256[]", "internalType": "uint256[]" }], "stateMutability": "view" }, { "type": "function", "name": "proxiableUUID", "inputs": [], "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], "stateMutability": "view" }, { "type": "function", "name": "requestedFee", "inputs": [], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "revokeSignature", "inputs": [{ "name": "id", "type": "uint256", "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "internalType": "bytes32" }, { "name": "signatureHash", "type": "bytes32", "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "sendOffChain", "inputs": [{ "name": "emitter", "type": "bytes32", "internalType": "bytes32" }, { "name": "recipients", "type": "tuple[]", "internalType": "struct IWeb3Doc.Recipient[]", "components": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "signatureRequested", "type": "bool", "internalType": "bool" }] }, { "name": "dochash", "type": "bytes32", "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "internalType": "bytes" }, { "name": "uri", "type": "string", "internalType": "string" }, { "name": "mimeType", "type": "string", "internalType": "string" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "sendOnChain", "inputs": [{ "name": "emitter", "type": "bytes32", "internalType": "bytes32" }, { "name": "recipients", "type": "tuple[]", "internalType": "struct IWeb3Doc.Recipient[]", "components": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }, { "name": "signatureRequested", "type": "bool", "internalType": "bool" }] }, { "name": "dochash", "type": "bytes32", "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "internalType": "bytes" }, { "name": "document", "type": "bytes", "internalType": "bytes" }, { "name": "mimeType", "type": "string", "internalType": "string" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "setAuthority", "inputs": [{ "name": "newAuthority", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "sign", "inputs": [{ "name": "id", "type": "uint256", "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "timestamp", "inputs": [{ "name": "emitter", "type": "bytes32", "internalType": "bytes32" }, { "name": "dochash", "type": "bytes32", "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "updateRequestedFee", "inputs": [{ "name": "newFee", "type": "uint256", "internalType": "uint256" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "upgradeToAndCall", "inputs": [{ "name": "newImplementation", "type": "address", "internalType": "address" }, { "name": "data", "type": "bytes", "internalType": "bytes" }], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "withdrawFees", "inputs": [{ "name": "to", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "event", "name": "AuthorityUpdated", "inputs": [{ "name": "authority", "type": "address", "indexed": false, "internalType": "address" }], "anonymous": false }, { "type": "event", "name": "Copy", "inputs": [{ "name": "copy", "type": "uint256", "indexed": true, "internalType": "uint256" }, { "name": "original", "type": "uint256", "indexed": true, "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "document", "type": "bytes", "indexed": false, "internalType": "bytes" }, { "name": "uri", "type": "string", "indexed": false, "internalType": "string" }], "anonymous": false }, { "type": "event", "name": "Document", "inputs": [{ "name": "id", "type": "uint256", "indexed": true, "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "dochash", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "indexed": false, "internalType": "bytes" }, { "name": "document", "type": "bytes", "indexed": false, "internalType": "bytes" }, { "name": "uri", "type": "string", "indexed": false, "internalType": "string" }, { "name": "mimeType", "type": "string", "indexed": false, "internalType": "string" }], "anonymous": false }, { "type": "event", "name": "FeesWithdrawn", "inputs": [{ "name": "to", "type": "address", "indexed": true, "internalType": "address" }, { "name": "amount", "type": "uint256", "indexed": false, "internalType": "uint256" }], "anonymous": false }, { "type": "event", "name": "Initialized", "inputs": [{ "name": "version", "type": "uint64", "indexed": false, "internalType": "uint64" }], "anonymous": false }, { "type": "event", "name": "Notification", "inputs": [{ "name": "id", "type": "uint256", "indexed": true, "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "indexed": false, "internalType": "bytes32" }, { "name": "recipient", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "source", "type": "uint8", "indexed": false, "internalType": "enum IWeb3Doc.EventType" }, { "name": "signatureRequested", "type": "bool", "indexed": true, "internalType": "bool" }], "anonymous": false }, { "type": "event", "name": "RequestedFeeUpdated", "inputs": [{ "name": "oldFee", "type": "uint256", "indexed": false, "internalType": "uint256" }, { "name": "newFee", "type": "uint256", "indexed": false, "internalType": "uint256" }], "anonymous": false }, { "type": "event", "name": "Signature", "inputs": [{ "name": "id", "type": "uint256", "indexed": true, "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "signatureHash", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "SignatureRevocation", "inputs": [{ "name": "id", "type": "uint256", "indexed": true, "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "signatureHash", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "Timestamp", "inputs": [{ "name": "id", "type": "uint256", "indexed": true, "internalType": "uint256" }, { "name": "emitter", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "dochash", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "signature", "type": "bytes", "indexed": false, "internalType": "bytes" }], "anonymous": false }, { "type": "event", "name": "Upgraded", "inputs": [{ "name": "implementation", "type": "address", "indexed": true, "internalType": "address" }], "anonymous": false }, { "type": "error", "name": "AccessManagedInvalidAuthority", "inputs": [{ "name": "authority", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "AccessManagedRequiredDelay", "inputs": [{ "name": "caller", "type": "address", "internalType": "address" }, { "name": "delay", "type": "uint32", "internalType": "uint32" }] }, { "type": "error", "name": "AccessManagedUnauthorized", "inputs": [{ "name": "caller", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "AddressEmptyCode", "inputs": [{ "name": "target", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "DocumentIsACopy", "inputs": [{ "name": "id", "type": "uint256", "internalType": "uint256" }] }, { "type": "error", "name": "DocumentNotFound", "inputs": [{ "name": "id", "type": "uint256", "internalType": "uint256" }] }, { "type": "error", "name": "ERC1967InvalidImplementation", "inputs": [{ "name": "implementation", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "ERC1967NonPayable", "inputs": [] }, { "type": "error", "name": "EmitterNotFound", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }] }, { "type": "error", "name": "FailedCall", "inputs": [] }, { "type": "error", "name": "FeeRequired", "inputs": [{ "name": "provided", "type": "uint256", "internalType": "uint256" }, { "name": "required", "type": "uint256", "internalType": "uint256" }] }, { "type": "error", "name": "FeesWithdrawalFailed", "inputs": [] }, { "type": "error", "name": "InvalidInitialization", "inputs": [] }, { "type": "error", "name": "NoDirectPaymentsAllowed", "inputs": [] }, { "type": "error", "name": "NoFeesToWithdraw", "inputs": [] }, { "type": "error", "name": "NotInitializing", "inputs": [] }, { "type": "error", "name": "RecipientNotFound", "inputs": [{ "name": "fingerprint", "type": "bytes32", "internalType": "bytes32" }] }, { "type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": [] }, { "type": "error", "name": "SignatureNotFound", "inputs": [{ "name": "signatureHash", "type": "bytes32", "internalType": "bytes32" }] }, { "type": "error", "name": "UUPSUnauthorizedCallContext", "inputs": [] }, { "type": "error", "name": "UUPSUnsupportedProxiableUUID", "inputs": [{ "name": "slot", "type": "bytes32", "internalType": "bytes32" }] }];
2681
+
2682
+ // src/web3doc/types/errors.ts
2683
+ var Web3DocError = class extends Error {
2684
+ /**
2685
+ * Creates a new Web3DocError instance.
2686
+ * @param message - Human-readable error description
2687
+ * @param cause - Optional underlying error that caused this error
2688
+ */
2689
+ constructor(message, cause) {
2690
+ super(message);
2691
+ this.name = "Web3DocError";
2692
+ this.cause = cause;
2693
+ if (Error.captureStackTrace) {
2694
+ Error.captureStackTrace(this, this.constructor);
2695
+ }
2696
+ }
2697
+ };
2698
+ var Web3DocCriticalError = class extends Web3DocError {
2699
+ /**
2700
+ * Creates a new Web3DocCriticalError instance.
2701
+ * @param message - Human-readable error description
2702
+ * @param cause - Optional underlying error that caused this error
2703
+ */
2704
+ constructor(message, cause) {
2705
+ super(message, cause);
2706
+ this.name = "Web3DocCriticalError";
2707
+ }
2708
+ };
2709
+
2710
+ // src/web3doc/web3doc.ts
2711
+ var _Web3Doc = class _Web3Doc extends FlatFee2 {
2712
+ /**
2713
+ * Creates a new Web3Doc instance.
2714
+ *
2715
+ * @param address The address of the Web3Doc smart contract.
2716
+ * @param web3pgp An instance implementing the IWeb3PGP interface for public key operations.
2717
+ * @param client A Viem public client for interacting with the blockchain.
2718
+ * @param walletClient Optional Viem wallet client for signing transactions.
2719
+ */
2720
+ constructor(address, web3pgp, client, walletClient) {
2721
+ super(address, client, walletClient);
2722
+ }
2723
+ /*****************************************************************************************************************/
2724
+ /* WRITE FUNCTIONS */
2725
+ /*****************************************************************************************************************/
2726
+ /**
2727
+ * Sends a document and its detached signature on-chain and notifies the specified recipients.
2728
+ *
2729
+ * @param emitter The fingerprint of the key used by the emitter to produce the signature.
2730
+ * @param recipients The list of recipients to notify and optionally prompt to sign the document.
2731
+ * @param dochash The declared keccak256 hash of the document.
2732
+ * @param signature A detached binary OpenPGP signature of the document.
2733
+ * @param document The binary OpenPGP message which contains the document.
2734
+ * @param mimeType Optional, The MIME type of the document and additional attributes (RFC6838)
2735
+ */
2736
+ async sendOnChain(emitter, recipients, dochash, signature, document, mimeType) {
2737
+ this.ensureWalletClient();
2738
+ const fee = await this.requestedFee();
2739
+ const { request } = await this.client.simulateContract({
2740
+ address: this.address,
2741
+ account: this.walletClient.account,
2742
+ abi: Web3Doc,
2743
+ functionName: "sendOnChain",
2744
+ args: [
2745
+ toBytes32(to0x(emitter)),
2746
+ recipients.map((recipient) => ({
2747
+ fingerprint: toBytes32(to0x(recipient.fingerprint)),
2748
+ signatureRequested: recipient.signatureRequested
2749
+ })),
2750
+ dochash,
2751
+ signature,
2752
+ document,
2753
+ mimeType
2754
+ ],
2755
+ value: fee
2756
+ // Include fee in the transaction value
2757
+ });
2758
+ const txhash = await this.walletClient.writeContract(request);
2759
+ return this.client.waitForTransactionReceipt({ hash: txhash });
2760
+ }
2761
+ /**
2762
+ * Sends a document using an off-chain channel, publishes its detached signature on-chain and notifies the specified recipients.
2763
+ *
2764
+ * @param emitter The fingerprint of the key used to produce the signature.
2765
+ * @param recipients The list of recipients to notify and optionally prompt to sign the document.
2766
+ * @param dochash The declared keccak256 hash of the document.
2767
+ * @param signature A detached binary OpenPGP signature of the document.
2768
+ * @param uri A URI which can be used to download the OpenPGP message (compressed and encrypted) which contains the document.
2769
+ * @param mimeType Optional, The MIME type of the document and additional attributes (RFC6838)
2770
+ */
2771
+ async sendOffChain(emitter, recipients, dochash, signature, uri, mimeType) {
2772
+ this.ensureWalletClient();
2773
+ const fee = await this.requestedFee();
2774
+ const { request } = await this.client.simulateContract({
2775
+ address: this.address,
2776
+ account: this.walletClient.account,
2777
+ abi: Web3Doc,
2778
+ functionName: "sendOffChain",
2779
+ args: [
2780
+ toBytes32(to0x(emitter)),
2781
+ recipients.map((recipient) => ({
2782
+ fingerprint: toBytes32(to0x(recipient.fingerprint)),
2783
+ signatureRequested: recipient.signatureRequested
2784
+ })),
2785
+ dochash,
2786
+ signature,
2787
+ uri,
2788
+ mimeType
2789
+ ],
2790
+ value: fee
2791
+ // Include fee in the transaction value
2792
+ });
2793
+ const txhash = await this.walletClient.writeContract(request);
2794
+ return this.client.waitForTransactionReceipt({ hash: txhash });
2795
+ }
2796
+ /**
2797
+ * Send a certified copy of a document on-chain.
2798
+ *
2799
+ * @param original The ID of the original document that is the subject of the copy.
2800
+ * @param emitter The fingerprint of the emitter's public key.
2801
+ * @param recipients The list of recipient key fingerprints to be notified and, optionally, be prompted for a signature.
2802
+ * @param document The binary OpenPGP message containing the copy of the original document.
2803
+ */
2804
+ async copyOnChain(original, emitter, recipients, document) {
2805
+ this.ensureWalletClient();
2806
+ const fee = await this.requestedFee();
2807
+ const { request } = await this.client.simulateContract({
2808
+ address: this.address,
2809
+ account: this.walletClient.account,
2810
+ abi: Web3Doc,
2811
+ functionName: "copyOnChain",
2812
+ args: [
2813
+ original,
2814
+ toBytes32(to0x(emitter)),
2815
+ recipients.map((recipient) => ({
2816
+ fingerprint: toBytes32(to0x(recipient.fingerprint)),
2817
+ signatureRequested: recipient.signatureRequested
2818
+ })),
2819
+ document
2820
+ ],
2821
+ value: fee
2822
+ // Include fee in the transaction value
2823
+ });
2824
+ const txhash = await this.walletClient.writeContract(request);
2825
+ return this.client.waitForTransactionReceipt({ hash: txhash });
2826
+ }
2827
+ /**
2828
+ * Send a certified copy of a document using an off-chain storage.
2829
+ *
2830
+ * @param original The ID of the original document being copied. Must reference a valid, non-copy document.
2831
+ * @param emitter The fingerprint of the key used to produce the signature.
2832
+ * @param recipients The list of recipient key fingerprints to be notified and, optionally, be prompted for a signature.
2833
+ * @param uri A URI which can be used to download the OpenPGP message containing the (compressed, encrypted and signed) document itself.
2834
+ */
2835
+ async copyOffChain(original, emitter, recipients, uri) {
2836
+ this.ensureWalletClient();
2837
+ const fee = await this.requestedFee();
2838
+ const { request } = await this.client.simulateContract({
2839
+ address: this.address,
2840
+ account: this.walletClient.account,
2841
+ abi: Web3Doc,
2842
+ functionName: "copyOffChain",
2843
+ args: [
2844
+ original,
2845
+ toBytes32(to0x(emitter)),
2846
+ recipients.map((recipient) => ({
2847
+ fingerprint: toBytes32(to0x(recipient.fingerprint)),
2848
+ signatureRequested: recipient.signatureRequested
2849
+ })),
2850
+ uri
2851
+ ],
2852
+ value: fee
2853
+ // Include fee in the transaction value
2854
+ });
2855
+ const txhash = await this.walletClient.writeContract(request);
2856
+ return this.client.waitForTransactionReceipt({ hash: txhash });
2857
+ }
2858
+ /**
2859
+ * Publishes a binary detached OpenPGP signature of a document made with the emitter's key.
2860
+ *
2861
+ * @param id The unique ID of the document that has been signed.
2862
+ * @param emitter The fingerprint of the key used to produce the signature.
2863
+ * @param signature The detached binary OpenPGP signature made over the document.
2864
+ */
2865
+ async sign(id, emitter, signature) {
2866
+ this.ensureWalletClient();
2867
+ const fee = await this.requestedFee();
2868
+ const { request } = await this.client.simulateContract({
2869
+ address: this.address,
2870
+ account: this.walletClient.account,
2871
+ abi: Web3Doc,
2872
+ functionName: "sign",
2873
+ args: [
2874
+ id,
2875
+ toBytes32(to0x(emitter)),
2876
+ signature
2877
+ ],
2878
+ value: fee
2879
+ // Include fee in the transaction value
2880
+ });
2881
+ const txhash = await this.walletClient.writeContract(request);
2882
+ return this.client.waitForTransactionReceipt({ hash: txhash });
2883
+ }
2884
+ /**
2885
+ * Timestamps a document by publishing a detached signature of the hash of the document on-chain.
2886
+ *
2887
+ * @param emitter The fingerprint of the key used to produce the signature.
2888
+ * @param dochash The keccak256 hash of the document used to find the timestamp from the document and verify their integrity.
2889
+ * @param signature A detached binary OpenPGP signature made over the raw bytes of the keccak256 hash of the document.
2890
+ */
2891
+ async timestamp(emitter, dochash, signature) {
2892
+ this.ensureWalletClient();
2893
+ const fee = await this.requestedFee();
2894
+ const { request } = await this.client.simulateContract({
2895
+ address: this.address,
2896
+ account: this.walletClient.account,
2897
+ abi: Web3Doc,
2898
+ functionName: "timestamp",
2899
+ args: [
2900
+ toBytes32(to0x(emitter)),
2901
+ dochash,
2902
+ signature
2903
+ ],
2904
+ value: fee
2905
+ // Include fee in the transaction value
2906
+ });
2907
+ const txhash = await this.walletClient.writeContract(request);
2908
+ return this.client.waitForTransactionReceipt({ hash: txhash });
2909
+ }
2910
+ /**
2911
+ * Revokes a signature previously published on-chain.
2912
+ *
2913
+ * @param id The ID of the document associated with the signature.
2914
+ * @param emitter The fingerprint of the key that made the signature.
2915
+ * @param signatureHash The hash of the signature to revoke.
2916
+ * @param signature A detached binary OpenPGP signature made over the raw bytes of the signature hash.
2917
+ */
2918
+ async revokeSignature(id, emitter, signatureHash, signature) {
2919
+ this.ensureWalletClient();
2920
+ const fee = await this.requestedFee();
2921
+ const { request } = await this.client.simulateContract({
2922
+ address: this.address,
2923
+ account: this.walletClient.account,
2924
+ abi: Web3Doc,
2925
+ functionName: "revokeSignature",
2926
+ args: [
2927
+ id,
2928
+ toBytes32(to0x(emitter)),
2929
+ signatureHash,
2930
+ signature
2931
+ ],
2932
+ value: fee
2933
+ // Include fee in the transaction value
2934
+ });
2935
+ const txhash = await this.walletClient.writeContract(request);
2936
+ return this.client.waitForTransactionReceipt({ hash: txhash });
2937
+ }
2938
+ /*****************************************************************************************************************/
2939
+ /* READ FUNCTIONS */
2940
+ /*****************************************************************************************************************/
2941
+ /**
2942
+ * Get the address of the Web3PGP contract used as a global public key registry.
2943
+ *
2944
+ * @return The address of the Web3PGP contract used by this contract.
2945
+ */
2946
+ async getWeb3PGPAddress() {
2947
+ return this.client.readContract({
2948
+ address: this.address,
2949
+ abi: Web3Doc,
2950
+ functionName: "getWeb3PGPAddress"
2951
+ });
2952
+ }
2953
+ /**
2954
+ * Returns the ID of the original document that the given document is a copy of.
2955
+ *
2956
+ * @param id The ID of a document that may be a copy of another previously published document.
2957
+ * @return The ID of the original document if the given document is a copy, or 0 if it is not a copy.
2958
+ */
2959
+ async isCopyOf(id) {
2960
+ return this.client.readContract({
2961
+ address: this.address,
2962
+ abi: Web3Doc,
2963
+ functionName: "isCopyOf",
2964
+ args: [id]
2965
+ });
2966
+ }
2967
+ /**
2968
+ * Returns the block number in which the document or timestamps with the given ID was published.
2969
+ *
2970
+ * @param id The ID of the document whose block number is to be retrieved.
2971
+ * @return The block number in which the document was published. 0 if the document does not exist.
2972
+ */
2973
+ async getDocumentBlockNumberByID(id) {
2974
+ return this.client.readContract({
2975
+ address: this.address,
2976
+ abi: Web3Doc,
2977
+ functionName: "getDocumentBlockNumberByID",
2978
+ args: [id]
2979
+ });
2980
+ }
2981
+ /**
2982
+ * Returns the block numbers in which the documents or timestamps with the given IDs were published.
2983
+ *
2984
+ * @param ids The IDs of the documents whose block numbers are to be retrieved.
2985
+ * @return The block numbers in which the documents were published.
2986
+ */
2987
+ async getDocumentBlockNumberByIDBatch(ids) {
2988
+ return this.client.readContract({
2989
+ address: this.address,
2990
+ abi: Web3Doc,
2991
+ functionName: "getDocumentBlockNumberByIDBatch",
2992
+ args: [ids]
2993
+ });
2994
+ }
2995
+ /**
2996
+ * Lists the block numbers when were published the signatures associated with the given document.
2997
+ *
2998
+ * @param id The ID of the document whose signatures are to be listed.
2999
+ * @param start The starting index from which to list signatures (0-based).
3000
+ * @param limit The maximum number of signatures to list.
3001
+ * @return An array of signature IDs associated with the document.
3002
+ */
3003
+ async listSignatures(id, start, limit) {
3004
+ return this.client.readContract({
3005
+ address: this.address,
3006
+ abi: Web3Doc,
3007
+ functionName: "listSignatures",
3008
+ args: [id, start, limit]
3009
+ });
3010
+ }
3011
+ /**
3012
+ * Returns the block number in which the signature with the given hash was created or 0 if the signature does not exist.
3013
+ *
3014
+ * @param signatureHash The hash of the signature.
3015
+ * @return The block number in which the signature was created, or 0 if the signature does not exist.
3016
+ */
3017
+ async getSignatureBlockNumberByHash(signatureHash) {
3018
+ return this.client.readContract({
3019
+ address: this.address,
3020
+ abi: Web3Doc,
3021
+ functionName: "getSignatureBlockNumberByHash",
3022
+ args: [signatureHash]
3023
+ });
3024
+ }
3025
+ /**
3026
+ * Returns the block numbers in which the signatures with the given hashes were created.
3027
+ *
3028
+ * @param signatureHashes The hashes of the signatures.
3029
+ * @return The block numbers in which the signatures were created.
3030
+ */
3031
+ async getSignatureBlockNumberByHashBatch(signatureHashes) {
3032
+ return this.client.readContract({
3033
+ address: this.address,
3034
+ abi: Web3Doc,
3035
+ functionName: "getSignatureBlockNumberByHashBatch",
3036
+ args: [signatureHashes]
3037
+ });
3038
+ }
3039
+ /**
3040
+ * Lists the document IDs by the given document hash.
3041
+ *
3042
+ * @param dochash The hash of the document.
3043
+ * @param start The starting index from which to list documents (0-based).
3044
+ * @param limit The maximum number of documents to list.
3045
+ * @return An array of document IDs with the given hash.
3046
+ */
3047
+ async listDocumentIdsByHash(dochash, start, limit) {
3048
+ return this.client.readContract({
3049
+ address: this.address,
3050
+ abi: Web3Doc,
3051
+ functionName: "listDocumentIdsByHash",
3052
+ args: [dochash, start, limit]
3053
+ });
3054
+ }
3055
+ /**
3056
+ * Lists the block numbers of signature revocations for the given signature hash.
3057
+ *
3058
+ * @param signatureHash The hash of the signature.
3059
+ * @param start The starting index from which to list revocations (0-based).
3060
+ * @param limit The maximum number of revocations to list.
3061
+ * @return An array of block numbers where the signature was revoked.
3062
+ */
3063
+ async listSignatureRevocationsBlockNumbers(signatureHash, start, limit) {
3064
+ return this.client.readContract({
3065
+ address: this.address,
3066
+ abi: Web3Doc,
3067
+ functionName: "listSignatureRevocationsBlockNumbers",
3068
+ args: [signatureHash, start, limit]
3069
+ });
3070
+ }
3071
+ /*****************************************************************************************************************/
3072
+ /* LOGS SEARCH FUNCTIONS */
3073
+ /*****************************************************************************************************************/
3074
+ /**
3075
+ * Searches for Document events emitted by the smart contract, filtered by the provided criteria.
3076
+ * Each value in a filter is combined using a logical OR, while all defined filters are combined using a logical AND.
3077
+ *
3078
+ * @param ids Filter by document IDs. IDs uniqueness is guaranteed by the smart contract.
3079
+ * @param emitters Filter by emitter fingerprints.
3080
+ * @param dochashes Filter by document hashes.
3081
+ * @param fromBlock Filter events from this block number. Genesis block if not specified.
3082
+ * @param toBlock Filter events up to this block number. Latest block if not specified.
3083
+ * @returns The list of DocumentLog matching the provided filters.
3084
+ */
3085
+ async searchDocumentLogs(ids, emitters, dochashes, fromBlock, toBlock) {
3086
+ if (fromBlock === "pending" || toBlock === "pending") {
3087
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
3088
+ }
3089
+ const from = fromBlock ?? "earliest";
3090
+ const to = toBlock ?? "latest";
3091
+ let args = void 0;
3092
+ if (ids !== void 0 || emitters !== void 0 || dochashes !== void 0) {
3093
+ args = {};
3094
+ if (ids !== void 0) {
3095
+ args.id = ids;
3096
+ }
3097
+ if (emitters !== void 0) {
3098
+ args.emitter = emitters.map(toBytes32);
3099
+ }
3100
+ if (dochashes !== void 0) {
3101
+ args.dochash = dochashes;
3102
+ }
3103
+ }
3104
+ const logs = await this.client.getLogs({
3105
+ strict: true,
3106
+ address: this.address,
3107
+ event: _Web3Doc.DOCUMENT_EVENT,
3108
+ fromBlock: from,
3109
+ toBlock: to,
3110
+ ...args !== void 0 && { args }
3111
+ });
3112
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
3113
+ const blockTimestamps = new Map(
3114
+ await Promise.all(uniqueBlocks.map(
3115
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3116
+ ))
3117
+ );
3118
+ return logs.map((log) => ({
3119
+ blockNumber: log.blockNumber,
3120
+ blockHash: log.blockHash,
3121
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3122
+ transactionHash: log.transactionHash,
3123
+ logIndex: log.logIndex,
3124
+ type: Web3DocEvents.Document,
3125
+ id: log.args.id,
3126
+ emitter: log.args.emitter,
3127
+ dochash: log.args.dochash,
3128
+ signature: log.args.signature,
3129
+ document: log.args.document,
3130
+ uri: log.args.uri,
3131
+ mimeType: log.args.mimeType
3132
+ }));
3133
+ }
3134
+ /**
3135
+ * Retrieves a Document event by its unique ID.
3136
+ *
3137
+ * @param id The unique ID of the document.
3138
+ * @param blockNumber The block number where to search for the document.
3139
+ * @returns The DocumentLog if found, otherwise undefined.
3140
+ * @example
3141
+ * ```typescript
3142
+ * const targetID = 1n;
3143
+ * const blockNumber = await web3Doc.getDocumentBlockNumberByID(targetID);
3144
+ * const documentLog = await web3Doc.getDocumentLogByID(targetID, blockNumber);
3145
+ * ```
3146
+ */
3147
+ async getDocumentLogByID(id, blockNumber) {
3148
+ const logs = await this.searchDocumentLogs([id], void 0, void 0, blockNumber, blockNumber);
3149
+ if (logs.length === 1) return logs[0];
3150
+ if (logs.length === 0) return void 0;
3151
+ throw new Web3DocCriticalError(`Multiple Document logs found for document ID ${id} at block ${blockNumber}`);
3152
+ }
3153
+ /**
3154
+ * Searches for Copy events emitted by the smart contract, filtered by the provided criteria.
3155
+ * Each value in a filter is combined using a logical OR, while all defined filters are combined using a logical AND.
3156
+ *
3157
+ * @param copies Filter by copy IDs. Copy IDs uniqueness is guaranteed by the smart contract.
3158
+ * @param originals Filter by original document IDs.
3159
+ * @param emitters Filter by emitter fingerprints.
3160
+ * @param fromBlock Filter events from this block number. Genesis block if not specified.
3161
+ * @param toBlock Filter events up to this block number. Latest block if not specified.
3162
+ * @returns The list of CopyLog matching the provided filters.
3163
+ */
3164
+ async searchCopyLogs(copies, originals, emitters, fromBlock, toBlock) {
3165
+ if (fromBlock === "pending" || toBlock === "pending") {
3166
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
3167
+ }
3168
+ const from = fromBlock ?? "earliest";
3169
+ const to = toBlock ?? "latest";
3170
+ let args = void 0;
3171
+ if (copies !== void 0 || originals !== void 0 || emitters !== void 0) {
3172
+ args = {};
3173
+ if (copies !== void 0) {
3174
+ args.copy = copies;
3175
+ }
3176
+ if (originals !== void 0) {
3177
+ args.original = originals;
3178
+ }
3179
+ if (emitters !== void 0) {
3180
+ args.emitter = emitters.map(toBytes32);
3181
+ }
3182
+ }
3183
+ const logs = await this.client.getLogs({
3184
+ strict: true,
3185
+ address: this.address,
3186
+ event: _Web3Doc.COPY_EVENT,
3187
+ fromBlock: from,
3188
+ toBlock: to,
3189
+ ...args !== void 0 && { args }
3190
+ });
3191
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
3192
+ const blockTimestamps = new Map(
3193
+ await Promise.all(uniqueBlocks.map(
3194
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3195
+ ))
3196
+ );
3197
+ return logs.map((log) => ({
3198
+ blockNumber: log.blockNumber,
3199
+ blockHash: log.blockHash,
3200
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3201
+ transactionHash: log.transactionHash,
3202
+ logIndex: log.logIndex,
3203
+ type: Web3DocEvents.Copy,
3204
+ copy: log.args.copy,
3205
+ original: log.args.original,
3206
+ emitter: log.args.emitter,
3207
+ document: log.args.document,
3208
+ uri: log.args.uri
3209
+ }));
3210
+ }
3211
+ /**
3212
+ * Retrieves a Copy event by its unique ID.
3213
+ *
3214
+ * @param copy The unique ID of the copy.
3215
+ * @param blockNumber The block number where to search for the copy.
3216
+ * @returns The CopyLog if found, otherwise undefined.
3217
+ * @example
3218
+ * ```typescript
3219
+ * const targetCopyID = 1n;
3220
+ * const blockNumber = await web3Doc.getDocumentBlockNumberByID(targetCopyID);
3221
+ * const copyLog = await web3Doc.getCopyLogByID(targetCopyID, blockNumber);
3222
+ * ```
3223
+ */
3224
+ async getCopyLogByID(copy, blockNumber) {
3225
+ const logs = await this.searchCopyLogs([copy], void 0, void 0, blockNumber, blockNumber);
3226
+ if (logs.length === 1) return logs[0];
3227
+ if (logs.length === 0) return void 0;
3228
+ throw new Web3DocCriticalError(`Multiple Copy logs found for copy ID ${copy} at block ${blockNumber}`);
3229
+ }
3230
+ /**
3231
+ * Searches for Notification events emitted by the smart contract, filtered by the provided criteria.
3232
+ * Each value in a filter is combined using a logical OR, while all defined filters are combined using a logical AND.
3233
+ *
3234
+ * @param ids Filter by document IDs. Document IDs uniqueness is guaranteed by the smart contract.
3235
+ * @param recipients Filter by recipient fingerprints.
3236
+ * @param signatureRequested Filter by whether a signature was requested or not.
3237
+ * @param fromBlock Filter events from this block number. Genesis block if not specified.
3238
+ * @param toBlock Filter events up to this block number. Latest block if not specified.
3239
+ * @returns The list of NotificationLog matching the provided filters.
3240
+ */
3241
+ async searchNotificationLogs(ids, recipients, signatureRequested, fromBlock, toBlock) {
3242
+ if (fromBlock === "pending" || toBlock === "pending") {
3243
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
3244
+ }
3245
+ const from = fromBlock ?? "earliest";
3246
+ const to = toBlock ?? "latest";
3247
+ let args = void 0;
3248
+ if (ids !== void 0 || recipients !== void 0 || signatureRequested !== void 0) {
3249
+ args = {};
3250
+ if (ids !== void 0) {
3251
+ args.id = ids;
3252
+ }
3253
+ if (recipients !== void 0) {
3254
+ args.recipient = recipients.map(toBytes32);
3255
+ }
3256
+ if (signatureRequested !== void 0) {
3257
+ args.signatureRequested = signatureRequested;
3258
+ }
3259
+ }
3260
+ const logs = await this.client.getLogs({
3261
+ strict: true,
3262
+ address: this.address,
3263
+ event: _Web3Doc.NOTIFICATION_EVENT,
3264
+ fromBlock: from,
3265
+ toBlock: to,
3266
+ ...args !== void 0 && { args }
3267
+ });
3268
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
3269
+ const blockTimestamps = new Map(
3270
+ await Promise.all(uniqueBlocks.map(
3271
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3272
+ ))
3273
+ );
3274
+ return logs.map((log) => ({
3275
+ blockNumber: log.blockNumber,
3276
+ blockHash: log.blockHash,
3277
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3278
+ transactionHash: log.transactionHash,
3279
+ logIndex: log.logIndex,
3280
+ type: Web3DocEvents.Notification,
3281
+ id: log.args.id,
3282
+ recipient: log.args.recipient,
3283
+ emitter: log.args.emitter,
3284
+ source: log.args.source,
3285
+ signatureRequested: log.args.signatureRequested
3286
+ }));
3287
+ }
3288
+ /**
3289
+ * Retrieves a Notification event by its unique ID and recipient.
3290
+ *
3291
+ * @param id The unique ID of the document that is the subject of the notification.
3292
+ * @param recipient The fingerprint of the recipient who received the notification.
3293
+ * @param blockNumber The block number where to search for the notification.
3294
+ * @returns The NotificationLog if found, otherwise undefined.
3295
+ * @example
3296
+ * ```typescript
3297
+ * const targetID = 1n;
3298
+ * const recipientFingerprint = '0x...';
3299
+ * const blockNumber = await web3Doc.getDocumentBlockNumberByID(targetID);
3300
+ * const notificationLog = await web3Doc.getNotificationLog(targetID, recipientFingerprint, blockNumber);
3301
+ * ```
3302
+ */
3303
+ async getNotificationLog(id, recipient, blockNumber) {
3304
+ const logs = await this.searchNotificationLogs([id], [recipient], void 0, blockNumber, blockNumber);
3305
+ if (logs.length === 1) return logs[0];
3306
+ if (logs.length === 0) return void 0;
3307
+ throw new Web3DocCriticalError(`Multiple Notification logs found for document ID ${id} and recipient ${recipient} at block ${blockNumber}`);
3308
+ }
3309
+ /**
3310
+ * Searches for Signature events emitted by the smart contract, filtered by the provided criteria.
3311
+ * Each value in a filter is combined using a logical OR, while all defined filters are combined using a logical AND.
3312
+ *
3313
+ * @param ids Filter by signature IDs. Signature IDs uniqueness is guaranteed by the smart contract.
3314
+ * @param emitters Filter by emitter fingerprints.
3315
+ * @param signatureHashes Filter by signature hashes.
3316
+ * @param fromBlock Filter events from this block number. Genesis block if not specified.
3317
+ * @param toBlock Filter events up to this block number. Latest block if not specified.
3318
+ * @returns The list of SignatureLog matching the provided filters.
3319
+ */
3320
+ async searchSignatureLogs(ids, emitters, signatureHashes, fromBlock, toBlock) {
3321
+ if (fromBlock === "pending" || toBlock === "pending") {
3322
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
3323
+ }
3324
+ const from = fromBlock ?? "earliest";
3325
+ const to = toBlock ?? "latest";
3326
+ let args = void 0;
3327
+ if (ids !== void 0 || emitters !== void 0 || signatureHashes !== void 0) {
3328
+ args = {};
3329
+ if (ids !== void 0) {
3330
+ args.id = ids;
3331
+ }
3332
+ if (emitters !== void 0) {
3333
+ args.emitter = emitters.map(toBytes32);
3334
+ }
3335
+ if (signatureHashes !== void 0) {
3336
+ args.signatureHash = signatureHashes;
3337
+ }
3338
+ }
3339
+ const logs = await this.client.getLogs({
3340
+ strict: true,
3341
+ address: this.address,
3342
+ event: _Web3Doc.SIGNATURE_EVENT,
3343
+ fromBlock: from,
3344
+ toBlock: to,
3345
+ ...args !== void 0 && { args }
3346
+ });
3347
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
3348
+ const blockTimestamps = new Map(
3349
+ await Promise.all(uniqueBlocks.map(
3350
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3351
+ ))
3352
+ );
3353
+ return logs.map((log) => ({
3354
+ blockNumber: log.blockNumber,
3355
+ blockHash: log.blockHash,
3356
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3357
+ transactionHash: log.transactionHash,
3358
+ logIndex: log.logIndex,
3359
+ type: Web3DocEvents.Signature,
3360
+ id: log.args.id,
3361
+ emitter: log.args.emitter,
3362
+ signatureHash: log.args.signatureHash,
3363
+ signature: log.args.signature
3364
+ }));
3365
+ }
3366
+ /**
3367
+ * Retrieves Timestamp events emitted by the smart contract, filtered by the provided criteria.
3368
+ * Each value in a filter is combined using a logical OR, while all defined filters are combined using a logical AND.
3369
+ *
3370
+ * @param ids Filter by timestamp IDs. Timestamp IDs uniqueness is guaranteed by the smart contract.
3371
+ * @param emitters Filter by emitter fingerprints.
3372
+ * @param dochashes Filter by document hashes.
3373
+ * @param fromBlock Filter events from this block number. Genesis block if not specified.
3374
+ * @param toBlock Filter events up to this block number. Latest block if not specified.
3375
+ * @returns The list of TimestampLog matching the provided filters.
3376
+ */
3377
+ async searchTimestampLogs(ids, emitters, dochashes, fromBlock, toBlock) {
3378
+ if (fromBlock === "pending" || toBlock === "pending") {
3379
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
3380
+ }
3381
+ const from = fromBlock ?? "earliest";
3382
+ const to = toBlock ?? "latest";
3383
+ let args = void 0;
3384
+ if (ids !== void 0 || emitters !== void 0 || dochashes !== void 0) {
3385
+ args = {};
3386
+ if (ids !== void 0) {
3387
+ args.id = ids;
3388
+ }
3389
+ if (emitters !== void 0) {
3390
+ args.emitter = emitters.map(toBytes32);
3391
+ }
3392
+ if (dochashes !== void 0) {
3393
+ args.dochash = dochashes;
3394
+ }
3395
+ }
3396
+ const logs = await this.client.getLogs({
3397
+ strict: true,
3398
+ address: this.address,
3399
+ event: _Web3Doc.TIMESTAMP_EVENT,
3400
+ fromBlock: from,
3401
+ toBlock: to,
3402
+ ...args !== void 0 && { args }
3403
+ });
3404
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
3405
+ const blockTimestamps = new Map(
3406
+ await Promise.all(uniqueBlocks.map(
3407
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3408
+ ))
3409
+ );
3410
+ return logs.map((log) => ({
3411
+ blockNumber: log.blockNumber,
3412
+ blockHash: log.blockHash,
3413
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3414
+ transactionHash: log.transactionHash,
3415
+ logIndex: log.logIndex,
3416
+ type: Web3DocEvents.Timestamp,
3417
+ id: log.args.id,
3418
+ emitter: log.args.emitter,
3419
+ dochash: log.args.dochash,
3420
+ signature: log.args.signature
3421
+ }));
3422
+ }
3423
+ /**
3424
+ * Retrieves a Timestamp event by its unique ID.
3425
+ * @param id The unique ID of the timestamp.
3426
+ * @param blockNumber The block number where to search for the timestamp.
3427
+ * @returns The TimestampLog if found, otherwise undefined.
3428
+ */
3429
+ async getTimestampLogByID(id, blockNumber) {
3430
+ const logs = await this.searchTimestampLogs([id], void 0, void 0, blockNumber, blockNumber);
3431
+ if (logs.length === 1) return logs[0];
3432
+ if (logs.length === 0) return void 0;
3433
+ throw new Web3DocCriticalError(`Multiple Timestamp logs found for timestamp ID ${id} at block ${blockNumber}`);
3434
+ }
3435
+ /**
3436
+ * Extracts Document logs from a transaction receipt.
3437
+ * @param receipt The transaction receipt to extract logs from.
3438
+ * @param timestamp Optional timestamp to assign to all extracted logs. This is useful when the receipt is from a transaction included in the latest block or in a block that has not been indexed yet.
3439
+ * @returns A promise that resolves to an array of DocumentLog objects.
3440
+ */
3441
+ async extractDocumentLog(receipt, timestamp) {
3442
+ const parsedLogs = parseEventLogs({
3443
+ abi: Web3Doc,
3444
+ eventName: "Document",
3445
+ logs: receipt.logs
3446
+ });
3447
+ if (timestamp) {
3448
+ return parsedLogs.map((log) => ({
3449
+ blockNumber: log.blockNumber,
3450
+ blockHash: log.blockHash,
3451
+ blockTimestamp: timestamp,
3452
+ transactionHash: log.transactionHash,
3453
+ logIndex: log.logIndex,
3454
+ type: Web3DocEvents.Document,
3455
+ id: log.args.id,
3456
+ emitter: log.args.emitter,
3457
+ dochash: log.args.dochash,
3458
+ signature: log.args.signature,
3459
+ document: log.args.document,
3460
+ uri: log.args.uri,
3461
+ mimeType: log.args.mimeType
3462
+ }));
3463
+ }
3464
+ const uniqueBlocks = [...new Set(parsedLogs.map((l) => l.blockNumber))];
3465
+ const blockTimestamps = new Map(
3466
+ await Promise.all(uniqueBlocks.map(
3467
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3468
+ ))
3469
+ );
3470
+ return parsedLogs.map((log) => ({
3471
+ blockNumber: log.blockNumber,
3472
+ blockHash: log.blockHash,
3473
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3474
+ transactionHash: log.transactionHash,
3475
+ id: log.args.id,
3476
+ emitter: log.args.emitter,
3477
+ dochash: log.args.dochash,
3478
+ signature: log.args.signature,
3479
+ document: log.args.document,
3480
+ uri: log.args.uri,
3481
+ mimeType: log.args.mimeType,
3482
+ logIndex: log.logIndex,
3483
+ type: Web3DocEvents.Document
3484
+ }));
3485
+ }
3486
+ /**
3487
+ * Extracts Copy logs from a transaction receipt.
3488
+ * @param receipt The transaction receipt to extract logs from.
3489
+ * @returns A promise that resolves to an array of CopyLog objects.
3490
+ * @returns A promise that resolves to an array of CopyLog objects.
3491
+ */
3492
+ async extractCopyLog(receipt, timestamp) {
3493
+ const parsedLogs = parseEventLogs({
3494
+ abi: Web3Doc,
3495
+ eventName: "Copy",
3496
+ logs: receipt.logs
3497
+ });
3498
+ if (timestamp) {
3499
+ return parsedLogs.map((log) => ({
3500
+ blockNumber: log.blockNumber,
3501
+ blockHash: log.blockHash,
3502
+ blockTimestamp: timestamp,
3503
+ transactionHash: log.transactionHash,
3504
+ logIndex: log.logIndex,
3505
+ type: Web3DocEvents.Copy,
3506
+ copy: log.args.copy,
3507
+ original: log.args.original,
3508
+ emitter: log.args.emitter,
3509
+ document: log.args.document,
3510
+ uri: log.args.uri
3511
+ }));
3512
+ }
3513
+ const uniqueBlocks = [...new Set(parsedLogs.map((l) => l.blockNumber))];
3514
+ const blockTimestamps = new Map(
3515
+ await Promise.all(uniqueBlocks.map(
3516
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3517
+ ))
3518
+ );
3519
+ return parsedLogs.map((log) => ({
3520
+ blockNumber: log.blockNumber,
3521
+ blockHash: log.blockHash,
3522
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3523
+ transactionHash: log.transactionHash,
3524
+ logIndex: log.logIndex,
3525
+ type: Web3DocEvents.Copy,
3526
+ copy: log.args.copy,
3527
+ original: log.args.original,
3528
+ emitter: log.args.emitter,
3529
+ document: log.args.document,
3530
+ uri: log.args.uri
3531
+ }));
3532
+ }
3533
+ /**
3534
+ * Extracts Signature logs from a transaction receipt.
3535
+ * @param receipt The transaction receipt to extract logs from.
3536
+ * @param timestamp Optional timestamp to assign to all extracted logs. This is useful when the receipt is from a transaction included in the latest block or in a block that has not been indexed yet.
3537
+ * @returns A promise that resolves to an array of SignatureLog objects.
3538
+ */
3539
+ async extractSignatureLog(receipt, timestamp) {
3540
+ const parsedLogs = parseEventLogs({
3541
+ abi: Web3Doc,
3542
+ eventName: "Signature",
3543
+ logs: receipt.logs
3544
+ });
3545
+ if (timestamp) {
3546
+ return parsedLogs.map((log) => ({
3547
+ blockNumber: log.blockNumber,
3548
+ blockHash: log.blockHash,
3549
+ blockTimestamp: timestamp,
3550
+ transactionHash: log.transactionHash,
3551
+ logIndex: log.logIndex,
3552
+ type: Web3DocEvents.Signature,
3553
+ id: log.args.id,
3554
+ emitter: log.args.emitter,
3555
+ signatureHash: log.args.signatureHash,
3556
+ signature: log.args.signature
3557
+ }));
3558
+ }
3559
+ const uniqueBlocks = [...new Set(parsedLogs.map((l) => l.blockNumber))];
3560
+ const blockTimestamps = new Map(
3561
+ await Promise.all(uniqueBlocks.map(
3562
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3563
+ ))
3564
+ );
3565
+ return parsedLogs.map((log) => ({
3566
+ blockNumber: log.blockNumber,
3567
+ blockHash: log.blockHash,
3568
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3569
+ transactionHash: log.transactionHash,
3570
+ logIndex: log.logIndex,
3571
+ type: Web3DocEvents.Signature,
3572
+ id: log.args.id,
3573
+ emitter: log.args.emitter,
3574
+ signatureHash: log.args.signatureHash,
3575
+ signature: log.args.signature
3576
+ }));
3577
+ }
3578
+ /**
3579
+ * Extracts Timestamp logs from a transaction receipt.
3580
+ * @param receipt The transaction receipt to extract logs from.
3581
+ * @param timestamp Optional timestamp to assign to all extracted logs. This is useful when the receipt is from a transaction included in the latest block or in a block that has not been indexed yet.
3582
+ * @returns A promise that resolves to an array of TimestampLog objects.
3583
+ */
3584
+ async extractTimestampLog(receipt, timestamp) {
3585
+ const parsedLogs = parseEventLogs({
3586
+ abi: Web3Doc,
3587
+ eventName: "Timestamp",
3588
+ logs: receipt.logs
3589
+ });
3590
+ if (timestamp) {
3591
+ return parsedLogs.map((log) => ({
3592
+ blockNumber: log.blockNumber,
3593
+ blockHash: log.blockHash,
3594
+ blockTimestamp: timestamp,
3595
+ transactionHash: log.transactionHash,
3596
+ logIndex: log.logIndex,
3597
+ type: Web3DocEvents.Timestamp,
3598
+ id: log.args.id,
3599
+ emitter: log.args.emitter,
3600
+ dochash: log.args.dochash,
3601
+ signature: log.args.signature
3602
+ }));
3603
+ }
3604
+ const uniqueBlocks = [...new Set(parsedLogs.map((l) => l.blockNumber))];
3605
+ const blockTimestamps = new Map(
3606
+ await Promise.all(uniqueBlocks.map(
3607
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3608
+ ))
3609
+ );
3610
+ return parsedLogs.map((log) => ({
3611
+ blockNumber: log.blockNumber,
3612
+ blockHash: log.blockHash,
3613
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3614
+ transactionHash: log.transactionHash,
3615
+ logIndex: log.logIndex,
3616
+ type: Web3DocEvents.Timestamp,
3617
+ id: log.args.id,
3618
+ emitter: log.args.emitter,
3619
+ dochash: log.args.dochash,
3620
+ signature: log.args.signature
3621
+ }));
3622
+ }
3623
+ /**
3624
+ * Extracts Notification logs from a transaction receipt.
3625
+ * @param receipt The transaction receipt to extract logs from.
3626
+ * @param timestamp Optional timestamp to assign to all extracted logs. This is useful when the receipt is from a transaction included in the latest block or in a block that has not been indexed yet.
3627
+ * @returns A promise that resolves to an array of NotificationLog objects.
3628
+ */
3629
+ async extractNotificationLog(receipt, timestamp) {
3630
+ const parsedLogs = parseEventLogs({
3631
+ strict: true,
3632
+ abi: Web3Doc,
3633
+ eventName: "Notification",
3634
+ logs: receipt.logs
3635
+ });
3636
+ if (timestamp) {
3637
+ return parsedLogs.map((log) => ({
3638
+ blockNumber: log.blockNumber,
3639
+ blockHash: log.blockHash,
3640
+ blockTimestamp: timestamp,
3641
+ transactionHash: log.transactionHash,
3642
+ logIndex: log.logIndex,
3643
+ type: Web3DocEvents.Notification,
3644
+ id: log.args.id,
3645
+ recipient: log.args.recipient,
3646
+ emitter: log.args.emitter,
3647
+ source: log.args.source,
3648
+ signatureRequested: log.args.signatureRequested
3649
+ }));
3650
+ }
3651
+ const uniqueBlocks = [...new Set(parsedLogs.map((l) => l.blockNumber))];
3652
+ const blockTimestamps = new Map(
3653
+ await Promise.all(uniqueBlocks.map(
3654
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3655
+ ))
3656
+ );
3657
+ return parsedLogs.map((log) => ({
3658
+ blockNumber: log.blockNumber,
3659
+ blockHash: log.blockHash,
3660
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3661
+ transactionHash: log.transactionHash,
3662
+ logIndex: log.logIndex,
3663
+ type: Web3DocEvents.Notification,
3664
+ id: log.args.id,
3665
+ recipient: log.args.recipient,
3666
+ emitter: log.args.emitter,
3667
+ source: log.args.source,
3668
+ signatureRequested: log.args.signatureRequested
3669
+ }));
3670
+ }
3671
+ /**
3672
+ * Retireve signature revocation events emitted by the smart contract, filtered by the provided criteria.
3673
+ * Each value in a filter is combined using a logical OR, while all defined filters are combined using a logical AND.
3674
+ *
3675
+ * @param ids Filter by signature IDs. Signature IDs uniqueness is guaranteed by the smart contract.
3676
+ * @param emitters Filter by emitter fingerprints.
3677
+ * @param signatureHashes Filter by signature hashes.
3678
+ * @param fromBlock Filter events from this block number. Genesis block if not specified.
3679
+ * @param toBlock Filter events up to this block number. Latest block if not specified.
3680
+ * @returns The list of SignatureRevocationLog matching the provided filters.
3681
+ */
3682
+ async searchSignatureRevocationLogs(ids, emitters, signatureHashes, fromBlock, toBlock) {
3683
+ if (fromBlock === "pending" || toBlock === "pending") {
3684
+ throw new Error('fromBlock and toBlock cannot be "pending" for log searches');
3685
+ }
3686
+ const from = fromBlock ?? "earliest";
3687
+ const to = toBlock ?? "latest";
3688
+ let args = void 0;
3689
+ if (ids !== void 0 || emitters !== void 0 || signatureHashes !== void 0) {
3690
+ args = {};
3691
+ if (ids !== void 0) {
3692
+ args.id = ids;
3693
+ }
3694
+ if (emitters !== void 0) {
3695
+ args.emitter = emitters.map(toBytes32);
3696
+ }
3697
+ if (signatureHashes !== void 0) {
3698
+ args.signatureHash = signatureHashes;
3699
+ }
3700
+ }
3701
+ const logs = await this.client.getLogs({
3702
+ strict: true,
3703
+ address: this.address,
3704
+ event: _Web3Doc.SIGNATURE_REVOCATION_EVENT,
3705
+ fromBlock: from,
3706
+ toBlock: to,
3707
+ ...args !== void 0 && { args }
3708
+ });
3709
+ const uniqueBlocks = [...new Set(logs.map((l) => l.blockNumber))];
3710
+ const blockTimestamps = new Map(
3711
+ await Promise.all(uniqueBlocks.map(
3712
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3713
+ ))
3714
+ );
3715
+ return logs.map((log) => ({
3716
+ blockNumber: log.blockNumber,
3717
+ blockHash: log.blockHash,
3718
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3719
+ transactionHash: log.transactionHash,
3720
+ logIndex: log.logIndex,
3721
+ type: Web3DocEvents.SignatureRevocation,
3722
+ id: log.args.id,
3723
+ emitter: log.args.emitter,
3724
+ signatureHash: log.args.signatureHash,
3725
+ signature: log.args.signature
3726
+ }));
3727
+ }
3728
+ /**
3729
+ * Retrieves Timestamp events by document hash.
3730
+ * @param dochash The keccak256 hash of the document.
3731
+ * @returns An array of TimestampLog entries associated with the document hash.
3732
+ */
3733
+ async getTimestampLogsByHash(dochash) {
3734
+ let ids = await this.fetchAllPaginated(
3735
+ (start, limit) => this.listDocumentIdsByHash(dochash, start, limit)
3736
+ );
3737
+ if (ids.length === 0) {
3738
+ return [];
3739
+ }
3740
+ let blockNumbers = await this.getDocumentBlockNumberByIDBatch(ids);
3741
+ if (blockNumbers.length !== ids.length) {
3742
+ throw new Web3DocCriticalError("Mismatch between document IDs and block numbers length");
3743
+ }
3744
+ const timestampLogs = await Promise.all(
3745
+ blockNumbers.map(async (blockNumber) => {
3746
+ return this.searchTimestampLogs(ids, void 0, [dochash], blockNumber, blockNumber);
3747
+ })
3748
+ );
3749
+ return timestampLogs.flat();
3750
+ }
3751
+ extractSignatureRevocationLog(receipt, timestamp) {
3752
+ const parsedLogs = parseEventLogs({
3753
+ strict: true,
3754
+ abi: Web3Doc,
3755
+ eventName: "SignatureRevocation",
3756
+ logs: receipt.logs
3757
+ });
3758
+ if (timestamp) {
3759
+ return Promise.resolve(parsedLogs.map((log) => ({
3760
+ blockNumber: log.blockNumber,
3761
+ blockHash: log.blockHash,
3762
+ blockTimestamp: timestamp,
3763
+ transactionHash: log.transactionHash,
3764
+ logIndex: log.logIndex,
3765
+ type: Web3DocEvents.SignatureRevocation,
3766
+ id: log.args.id,
3767
+ emitter: log.args.emitter,
3768
+ signatureHash: log.args.signatureHash,
3769
+ signature: log.args.signature
3770
+ })));
3771
+ }
3772
+ const uniqueBlocks = [...new Set(parsedLogs.map((l) => l.blockNumber))];
3773
+ return Promise.all(uniqueBlocks.map(
3774
+ async (bn) => [bn, await getBlockTimestamp(this.client, bn)]
3775
+ )).then((blockTimestampArray) => {
3776
+ const blockTimestamps = new Map(blockTimestampArray);
3777
+ return parsedLogs.map((log) => ({
3778
+ blockNumber: log.blockNumber,
3779
+ blockHash: log.blockHash,
3780
+ blockTimestamp: blockTimestamps.get(log.blockNumber),
3781
+ transactionHash: log.transactionHash,
3782
+ logIndex: log.logIndex,
3783
+ type: Web3DocEvents.SignatureRevocation,
3784
+ id: log.args.id,
3785
+ emitter: log.args.emitter,
3786
+ signatureHash: log.args.signatureHash,
3787
+ signature: log.args.signature
3788
+ }));
3789
+ });
3790
+ }
3791
+ /*****************************************************************************************************************/
3792
+ /* UTILITY METHODS */
3793
+ /*****************************************************************************************************************/
3794
+ /**
3795
+ * Helper method to fetch all items from a paginated contract method.
3796
+ * @param fetchFn The paginated fetch function to call
3797
+ * @param limit The number of items to fetch per page
3798
+ * @param maxItems The maximum number of items to fetch in total (safety limit)
3799
+ * @returns An array containing all fetched items
3800
+ */
3801
+ async fetchAllPaginated(fetchFn, limit = 1000n, maxItems = 100000n) {
3802
+ const results = [];
3803
+ let start = 0n;
3804
+ do {
3805
+ const batch = await fetchFn(start, limit);
3806
+ results.push(...batch);
3807
+ if (batch.length < Number(limit) || results.length >= Number(maxItems)) {
3808
+ break;
3809
+ }
3810
+ start += limit;
3811
+ } while (true);
3812
+ return results;
3813
+ }
3814
+ };
3815
+ _Web3Doc.abi = Web3Doc;
3816
+ // Pre-computed event definitions for efficient log queries
3817
+ _Web3Doc.DOCUMENT_EVENT = Web3Doc.find((item) => item.type === "event" && item.name === "Document");
3818
+ _Web3Doc.COPY_EVENT = Web3Doc.find((item) => item.type === "event" && item.name === "Copy");
3819
+ _Web3Doc.NOTIFICATION_EVENT = Web3Doc.find((item) => item.type === "event" && item.name === "Notification");
3820
+ _Web3Doc.SIGNATURE_EVENT = Web3Doc.find((item) => item.type === "event" && item.name === "Signature");
3821
+ _Web3Doc.SIGNATURE_REVOCATION_EVENT = Web3Doc.find((item) => item.type === "event" && item.name === "SignatureRevocation");
3822
+ _Web3Doc.TIMESTAMP_EVENT = Web3Doc.find((item) => item.type === "event" && item.name === "Timestamp");
3823
+ var Web3Doc2 = _Web3Doc;
3824
+ var Web3DocServiceError = class extends Error {
3825
+ constructor(message) {
3826
+ super(message);
3827
+ this.name = "Web3DocServiceError";
3828
+ }
3829
+ };
3830
+ var Web3DocServiceCriticalError = class extends Web3DocServiceError {
3831
+ constructor(message, cause) {
3832
+ super(message);
3833
+ this.cause = cause;
3834
+ this.name = "Web3DocServiceCriticalError";
3835
+ }
3836
+ };
3837
+ var Web3DocServiceValidationError = class extends Web3DocServiceError {
3838
+ constructor(message, cause) {
3839
+ super(message);
3840
+ this.cause = cause;
3841
+ this.name = "Web3DocServiceValidationError";
3842
+ }
3843
+ };
3844
+ var Web3DocService = class {
3845
+ /**
3846
+ * Creates a new Web3DocService instance.
3847
+ *
3848
+ * @param web3doc An instance implementing the IWeb3Doc interface.
3849
+ * @param options Optional configuration options for the service.
3850
+ */
3851
+ constructor(web3doc, web3pgpService, options) {
3852
+ this._web3doc = web3doc;
3853
+ this._web3pgpService = web3pgpService;
3854
+ this._options = {
3855
+ concurrencyLimit: options?.concurrencyLimit ?? 10
3856
+ };
3857
+ }
3858
+ /**
3859
+ * Gets the Web3Doc instance.
3860
+ */
3861
+ get web3doc() {
3862
+ return this._web3doc;
3863
+ }
3864
+ /**
3865
+ * Sets the Web3Doc instance.
3866
+ */
3867
+ set web3doc(value) {
3868
+ this._web3doc = value;
3869
+ }
3870
+ /**
3871
+ * Gets the Web3PGP service instance.
3872
+ */
3873
+ get web3pgpService() {
3874
+ return this._web3pgpService;
3875
+ }
3876
+ /**
3877
+ * Sets the Web3PGP service instance.
3878
+ */
3879
+ set web3pgpService(value) {
3880
+ this._web3pgpService = value;
3881
+ }
3882
+ /**
3883
+ * Gets the service configuration options.
3884
+ */
3885
+ get options() {
3886
+ return this._options;
3887
+ }
3888
+ /**
3889
+ * Sets the service configuration options.
3890
+ */
3891
+ set options(value) {
3892
+ this._options = value;
3893
+ }
3894
+ /*****************************************************************************************************************/
3895
+ /* TIMESTAMPING */
3896
+ /*****************************************************************************************************************/
3897
+ /**
3898
+ * This function allows submitting the data needed to timestamp a document to the Web3Doc smart contract.
3899
+ *
3900
+ * The function will download and verify the emitter's public key using the Web3PGP contract and then it will verify
3901
+ * the provided detached signature over the keccak256 hash of the document. If the signature is valid, it will submit
3902
+ * the hash and the signature to the smart contract for timestamping.
3903
+ *
3904
+ * The service is expected to be configured with a Web3PGP service instance to enable automatic public key retrieval based
3905
+ * on their fingerprint.
3906
+ *
3907
+ * @param hash The keccak256 hash of the document to be timestamped, provided as a Uint8Array.
3908
+ * @param signature The detached OpenPGP signature over the document hash.
3909
+ * @param emitter The fingerprint of the public key used to create the signature. Will be used to download the public key from Web3PGP.
3910
+ * @return A promise that resolves to the ID assigned to the new timestamp and the transaction receipt.
3911
+ */
3912
+ async timestamp(hash, signature, emitter) {
3913
+ try {
3914
+ console.debug(`[Web3Doc Service] Retrieving public key for emitter fingerprint: ${emitter}`);
3915
+ const pk = await this.web3pgpService.getPublicKey(emitter);
3916
+ console.debug(`[Web3Doc Service] Verifying signature for timestamp with emitter fingerprint: ${emitter}`);
3917
+ try {
3918
+ await openpgp2.verify({
3919
+ message: await openpgp2.createMessage({ binary: hash }),
3920
+ signature,
3921
+ verificationKeys: pk,
3922
+ date: /* @__PURE__ */ new Date()
3923
+ // Use current date for verification
3924
+ });
3925
+ } catch (error) {
3926
+ throw new Web3DocServiceValidationError(`Signature verification failed for emitter fingerprint: ${emitter}`, error instanceof Error ? error : void 0);
3927
+ }
3928
+ console.debug(`[Web3Doc Service] Submitting timestamp to Web3Doc smart contract for emitter fingerprint: ${emitter}`);
3929
+ const receipt = await this.web3doc.timestamp(toBytes32(to0x(pk.getFingerprint())), toHex(hash), toHex(signature.write()));
3930
+ console.debug(`[Web3Doc Service] Timestamp transaction submitted successfully`);
3931
+ const logs = await this.web3doc.extractTimestampLog(receipt, /* @__PURE__ */ new Date());
3932
+ if (logs.length !== 1) {
3933
+ throw new Web3DocServiceCriticalError(`Unexpected number of timestamp logs in transaction receipt: ${logs.length}`);
3934
+ }
3935
+ console.debug(`[Web3Doc Service] Timestamp submitted successfully with ID: ${logs[0].id.toString()}`);
3936
+ return [logs[0].id, receipt];
3937
+ } catch (error) {
3938
+ if (error instanceof Web3PGPServiceValidationError) {
3939
+ throw new Web3DocServiceValidationError(`Emitter public key not valid for fingerprint: ${emitter}`, error);
3940
+ }
3941
+ console.error(error);
3942
+ throw new Web3DocServiceCriticalError("Failed to retrieve or verify emitter public key", error instanceof Error ? error : void 0);
3943
+ }
3944
+ }
3945
+ /**
3946
+ * Verifies a timestamp entry on the blockchain by its ID. The function retrieves the timestamp data from the
3947
+ * Web3Doc smart contract and verifies the detached signature over the document hash using the emitter's public key.
3948
+ *
3949
+ * The function returns the document hash, the signature, and the public key used for verification.
3950
+ *
3951
+ * The user can then compare the hash received from this function with the keccak256 hash they compute from their
3952
+ * document to ensure integrity.
3953
+ *
3954
+ * @param id The ID of the timestamp entry to verify.
3955
+ * @returns A promise that resolves to an object containing the document hash, signature, the public key, the transaction hash and the date of the timestamp.
3956
+ */
3957
+ async verifyTimestamp(id) {
3958
+ try {
3959
+ const blockNumber = await this.web3doc.getDocumentBlockNumberByID(id);
3960
+ const timestamp = await this.web3doc.getTimestampLogByID(id, blockNumber);
3961
+ if (!timestamp) {
3962
+ throw new Web3DocServiceValidationError(`Timestamp with ID ${id.toString()} not found on the blockchain`);
3963
+ }
3964
+ console.debug(`[Web3Doc Service] Retrieving public key for emitter fingerprint: ${timestamp.emitter}`);
3965
+ const pk = await this.web3pgpService.getPublicKey(timestamp.emitter);
3966
+ console.debug(`[Web3Doc Service] Verifying signature for timestamp ID: ${id.toString()}`);
3967
+ const signature = await openpgp2.readSignature({ binarySignature: hexToBytes(timestamp.signature) });
3968
+ try {
3969
+ await openpgp2.verify({
3970
+ message: await openpgp2.createMessage({ binary: hexToBytes(timestamp.dochash) }),
3971
+ signature,
3972
+ verificationKeys: pk,
3973
+ date: timestamp.blockTimestamp
3974
+ // Use the block timestamp for verification
3975
+ });
3976
+ } catch (error) {
3977
+ throw new Web3DocServiceValidationError(`Signature verification failed for timestamp ID: ${id.toString()}`, error instanceof Error ? error : void 0);
3978
+ }
3979
+ console.debug(`[Web3Doc Service] Timestamp ID: ${id.toString()} verified successfully`);
3980
+ return {
3981
+ documentHash: hexToBytes(timestamp.dochash),
3982
+ signature,
3983
+ publicKey: pk,
3984
+ tx: timestamp.transactionHash,
3985
+ date: timestamp.blockTimestamp
3986
+ };
3987
+ } catch (error) {
3988
+ if (error instanceof Web3PGPServiceValidationError) {
3989
+ throw new Web3DocServiceValidationError(`Emitter public key not valid`, error);
3990
+ }
3991
+ throw new Web3DocServiceCriticalError("Failed to retrieve or verify the timestamp", error instanceof Error ? error : void 0);
3992
+ }
3993
+ }
3994
+ /**
3995
+ * Finds all timestamp IDs associated with a given document hash. Timestamp IDs can be used to retrieve and verify
3996
+ * timestamp entries on the blockchain.
3997
+ *
3998
+ * @param hash The keccak256 hash of the document to search for.
3999
+ * @returns A promise that resolves to an array of timestamp IDs associated with the provided document hash.
4000
+ */
4001
+ async findTimestampsByHash(hash) {
4002
+ const timestamps = await this.web3doc.getTimestampLogsByHash(toHex(hash));
4003
+ return timestamps.map((t) => t.id);
4004
+ }
4005
+ /*****************************************************************************************************************/
4006
+ /* DOCUMENT EXCHANGE */
4007
+ /*****************************************************************************************************************/
4008
+ // TODO: Implement document exchange methods (sendDocument, sendCopy, signDocument, etc.)
4009
+ };
4010
+
4011
+ export { BYTES32_ZERO, EventType, FlatFee2 as FlatFee, HexTooLongError, InvalidHexError, Web3Doc2 as Web3Doc, Web3DocEvents, Web3DocService, Web3DocServiceCriticalError, Web3DocServiceError, Web3DocServiceValidationError, Web3PGP2 as Web3PGP, Web3PGPEvents, Web3PGPService, Web3PGPServiceCriticalError, Web3PGPServiceError, Web3PGPServiceValidationError, to0x, toBytes32 };
4012
+ //# sourceMappingURL=index.mjs.map
4013
+ //# sourceMappingURL=index.mjs.map