@cofhe/hardhat-plugin 0.7.0 → 0.8.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 CHANGED
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/index.ts
10
- import chalk3 from "chalk";
10
+ import chalk4 from "chalk";
11
11
  import path from "path";
12
12
  import fs from "fs";
13
13
  import "viem";
@@ -82,6 +82,7 @@ import "hardhat/types";
82
82
  import chalk from "chalk";
83
83
  import { Wallet } from "ethers";
84
84
  import {
85
+ MockCoFHEAddressBookArtifact,
85
86
  MockTaskManagerArtifact,
86
87
  MockACLArtifact,
87
88
  ACPTimestampRevokerArtifact,
@@ -90,14 +91,14 @@ import {
90
91
  MockThresholdNetworkArtifact
91
92
  } from "@cofhe/mock-contracts";
92
93
  import {
93
- TASK_MANAGER_ADDRESS as TASK_MANAGER_ADDRESS2,
94
+ TASK_MANAGER_ID,
94
95
  MOCKS_ZK_VERIFIER_SIGNER_ADDRESS,
95
96
  MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY,
96
97
  MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY
97
98
  } from "@cofhe/sdk";
98
99
 
99
100
  // src/utils.ts
100
- import { TASK_MANAGER_ADDRESS, MOCKS_ZK_VERIFIER_ADDRESS } from "@cofhe/sdk";
101
+ import { MOCKS_TASK_MANAGER_ADDRESS, MOCKS_ZK_VERIFIER_ADDRESS } from "@cofhe/sdk";
101
102
  import { expect } from "chai";
102
103
  import { ethers } from "ethers";
103
104
  import "@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider";
@@ -122,7 +123,7 @@ var getFixedMockContract = async (hre, artifact) => {
122
123
  };
123
124
  var mock_checkIsTestnet = async (fnName, provider) => {
124
125
  const bytecode = await provider.getCode(MOCKS_ZK_VERIFIER_ADDRESS);
125
- const isTestnet = bytecode.length === 0;
126
+ const isTestnet = !bytecode || bytecode.length <= 2;
126
127
  if (isTestnet) {
127
128
  console.log(`${fnName} - skipped on non-testnet chain`);
128
129
  }
@@ -132,7 +133,7 @@ var mock_getPlaintext = async (provider, ctHash) => {
132
133
  if (await mock_checkIsTestnet(mock_getPlaintext.name, provider))
133
134
  return;
134
135
  const taskManager = new ethers.Contract(
135
- TASK_MANAGER_ADDRESS,
136
+ MOCKS_TASK_MANAGER_ADDRESS,
136
137
  ["function mockStorage(uint256) view returns (uint256)"],
137
138
  provider
138
139
  );
@@ -143,7 +144,7 @@ var mock_getPlaintextExists = async (provider, ctHash) => {
143
144
  if (await mock_checkIsTestnet(mock_getPlaintextExists.name, provider))
144
145
  return;
145
146
  const taskManager = new ethers.Contract(
146
- TASK_MANAGER_ADDRESS,
147
+ MOCKS_TASK_MANAGER_ADDRESS,
147
148
  ["function inMockStorage(uint256) view returns (bool)"],
148
149
  provider
149
150
  );
@@ -172,8 +173,12 @@ var deployMocks = async (hre, options = {
172
173
  }
173
174
  await hre.run("compile", { quiet: true });
174
175
  log("vv", chalk.bold("cofhe-hardhat-plugin :: deploy mocks"), 0);
176
+ const addressBook = await deployMockAddressBook(hre);
177
+ logDeployment("MockCoFHEAddressBook", await addressBook.getAddress());
175
178
  const taskManager = await deployMockTaskManager(hre);
176
179
  logDeployment("MockTaskManager", await taskManager.getAddress());
180
+ await registerTaskManager(addressBook, taskManager);
181
+ log("vv", "TaskManager registered in CoFHEAddressBook", 2);
177
182
  const acl = await deployMockACL(hre);
178
183
  logDeployment("MockACL", await acl.getAddress());
179
184
  const acpRevoker = await deployMockContractFromArtifact(hre, ACPTimestampRevokerArtifact);
@@ -185,7 +190,7 @@ var deployMocks = async (hre, options = {
185
190
  await (await acl.setShareRegistry(await acpShareRegistry.getAddress())).wait();
186
191
  log("vv", "Share registry set in ACL", 2);
187
192
  await linkTaskManagerAndACL(taskManager, acl);
188
- log("vv", "ACL address set in TaskManager", 2);
193
+ log("vv", "ACL address set in TaskManager, TaskManager address set in ACL", 2);
189
194
  await setVerifierSigner(taskManager);
190
195
  log("vv", "Verifier signer set", 2);
191
196
  await setDecryptResultSigner(taskManager);
@@ -196,13 +201,13 @@ var deployMocks = async (hre, options = {
196
201
  log("vv", `ETH balance: ${zkVerifierSignerBalance.toString()}`, 2);
197
202
  const zkVerifier = await deployMockZkVerifier(hre);
198
203
  logDeployment("MockZkVerifier", await zkVerifier.getAddress());
199
- const thresholdNetwork = await deployMockThresholdNetwork(hre, acl);
204
+ const thresholdNetwork = await deployMockThresholdNetwork(hre, taskManager, acl);
200
205
  logDeployment("MockThresholdNetwork", await thresholdNetwork.getAddress());
201
206
  log("v", chalk.bold("cofhe-hardhat-plugin :: mocks deployed"), 0);
202
207
  if (options.gasWarning) {
203
208
  logEmpty("v");
204
209
  logWarning(
205
- "When using mocks, FHE operations (eg FHE.add / FHE.mul) report a higher gas price due to additional on-chain mocking logic. Deploy your contracts on a testnet chain to check the true gas costs.\n(Disable this warning by setting 'cofhe.gasWarning: false' in your hardhat config)",
210
+ "When using mocks, FHE operations (eg FHE.add / FHE.mul) consume extra gas replicating off-chain CoFHE work on-chain. Use hre.cofhe.getAdjustedGasUsed(receipt) or set 'cofhe.gasSummary: true' to see gas numbers with that overhead excluded.\n(Disable this warning by setting 'cofhe.gasWarning: false' in your hardhat config)",
206
211
  0
207
212
  );
208
213
  }
@@ -211,11 +216,25 @@ var deployMocks = async (hre, options = {
211
216
  var getIsHardhat = async (hre) => {
212
217
  return hre.network.name === "hardhat";
213
218
  };
219
+ var deployMockAddressBook = async (hre) => {
220
+ const addressBook = await deployMockContractFromArtifact(hre, MockCoFHEAddressBookArtifact);
221
+ const bookExists = await addressBook.exists();
222
+ if (!bookExists) {
223
+ throw new Error("MockCoFHEAddressBook does not exist");
224
+ }
225
+ return addressBook;
226
+ };
227
+ var registerTaskManager = async (addressBook, taskManager) => {
228
+ const setTmTx = await addressBook.setTm(TASK_MANAGER_ID, await taskManager.getAddress());
229
+ await setTmTx.wait();
230
+ };
214
231
  var deployMockTaskManager = async (hre) => {
215
232
  const [signer] = await hre.ethers.getSigners();
216
233
  const taskManager = await deployMockContractFromArtifact(hre, MockTaskManagerArtifact);
217
234
  const initTx = await taskManager.initialize(signer.address);
218
235
  await initTx.wait();
236
+ const securityZonesTx = await taskManager.setSecurityZones(0, 1);
237
+ await securityZonesTx.wait();
219
238
  const tmExists = await taskManager.exists();
220
239
  if (!tmExists) {
221
240
  throw new Error("MockTaskManager does not exist");
@@ -244,6 +263,8 @@ var linkTaskManagerAndACL = async (taskManager, acl) => {
244
263
  const aclAddress = await acl.getAddress();
245
264
  const linkAclTx = await taskManager.setACLContract(aclAddress);
246
265
  await linkAclTx.wait();
266
+ const linkTaskManagerTx = await acl.setTaskManager(await taskManager.getAddress());
267
+ await linkTaskManagerTx.wait();
247
268
  };
248
269
  var setVerifierSigner = async (taskManager) => {
249
270
  const signer = new Wallet(MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY);
@@ -263,9 +284,9 @@ var deployMockZkVerifier = async (hre) => {
263
284
  }
264
285
  return zkVerifier;
265
286
  };
266
- var deployMockThresholdNetwork = async (hre, acl) => {
287
+ var deployMockThresholdNetwork = async (hre, taskManager, acl) => {
267
288
  const thresholdNetwork = await deployMockContractFromArtifact(hre, MockThresholdNetworkArtifact);
268
- const initTx = await thresholdNetwork.initialize(TASK_MANAGER_ADDRESS2, await acl.getAddress());
289
+ const initTx = await thresholdNetwork.initialize(await taskManager.getAddress(), await acl.getAddress());
269
290
  await initTx.wait();
270
291
  const exists = await thresholdNetwork.exists();
271
292
  if (!exists) {
@@ -349,10 +370,159 @@ var mock_withLogs = async (hre, closureName, closure) => {
349
370
  }
350
371
  };
351
372
 
373
+ // src/gas.ts
374
+ import chalk3 from "chalk";
375
+ import { ethers as ethers2 } from "ethers";
376
+ import { MOCKS_TASK_MANAGER_ADDRESS as MOCKS_TASK_MANAGER_ADDRESS2 } from "@cofhe/sdk";
377
+ var MOCK_GAS_CONSUMED_TOPIC = ethers2.id("MockGasConsumed(uint256)");
378
+ var mock_getAdjustedGasBreakdown = (receipt) => {
379
+ let mockGas = 0n;
380
+ let mockGasEvents = 0;
381
+ for (const log2 of receipt.logs ?? []) {
382
+ if (log2.address.toLowerCase() === MOCKS_TASK_MANAGER_ADDRESS2.toLowerCase() && log2.topics[0] === MOCK_GAS_CONSUMED_TOPIC) {
383
+ mockGas += BigInt(log2.data);
384
+ mockGasEvents += 1;
385
+ }
386
+ }
387
+ const gasUsed = BigInt(receipt.gasUsed);
388
+ return { gasUsed, mockGas, adjustedGasUsed: mockGas > gasUsed ? 0n : gasUsed - mockGas, mockGasEvents };
389
+ };
390
+ var mock_getAdjustedGasUsed = (receipt) => mock_getAdjustedGasBreakdown(receipt).adjustedGasUsed;
391
+ var shortAddress = (address) => `${address.slice(0, 6)}..${address.slice(-4)}`;
392
+ var buildSelectorNameMap = async (hre) => {
393
+ const selectorNames = /* @__PURE__ */ new Map();
394
+ for (const fqn of await hre.artifacts.getAllFullyQualifiedNames()) {
395
+ try {
396
+ const artifact = await hre.artifacts.readArtifact(fqn);
397
+ const iface = new ethers2.Interface(artifact.abi);
398
+ iface.forEachFunction((fn) => {
399
+ if (!selectorNames.has(fn.selector))
400
+ selectorNames.set(fn.selector, fn.format("sighash"));
401
+ });
402
+ } catch {
403
+ }
404
+ }
405
+ return selectorNames;
406
+ };
407
+ var buildAddressNameMap = async (hre, addresses) => {
408
+ const codeToName = /* @__PURE__ */ new Map();
409
+ for (const fqn of await hre.artifacts.getAllFullyQualifiedNames()) {
410
+ try {
411
+ const artifact = await hre.artifacts.readArtifact(fqn);
412
+ if (artifact.deployedBytecode && artifact.deployedBytecode !== "0x") {
413
+ codeToName.set(artifact.deployedBytecode.toLowerCase(), artifact.contractName);
414
+ }
415
+ } catch {
416
+ }
417
+ }
418
+ const addressNames = /* @__PURE__ */ new Map();
419
+ for (const address of addresses) {
420
+ try {
421
+ const code = await hre.network.provider.send("eth_getCode", [address, "latest"]);
422
+ addressNames.set(address, codeToName.get(code.toLowerCase()) ?? shortAddress(address));
423
+ } catch {
424
+ addressNames.set(address, shortAddress(address));
425
+ }
426
+ }
427
+ return addressNames;
428
+ };
429
+ var printMockGasSummary = async (hre) => {
430
+ if (hre.network.name !== "hardhat")
431
+ return;
432
+ let logs;
433
+ try {
434
+ logs = await hre.network.provider.send("eth_getLogs", [
435
+ { fromBlock: "0x0", toBlock: "latest", address: MOCKS_TASK_MANAGER_ADDRESS2, topics: [MOCK_GAS_CONSUMED_TOPIC] }
436
+ ]);
437
+ } catch {
438
+ return;
439
+ }
440
+ if (!logs || logs.length === 0)
441
+ return;
442
+ const mockGasPerTx = /* @__PURE__ */ new Map();
443
+ for (const log2 of logs) {
444
+ mockGasPerTx.set(log2.transactionHash, (mockGasPerTx.get(log2.transactionHash) ?? 0n) + BigInt(log2.data));
445
+ }
446
+ const aggregates = /* @__PURE__ */ new Map();
447
+ for (const [txHash, mockGas] of mockGasPerTx) {
448
+ try {
449
+ const receipt = await hre.network.provider.send("eth_getTransactionReceipt", [txHash]);
450
+ const tx = await hre.network.provider.send("eth_getTransactionByHash", [txHash]);
451
+ if (!receipt)
452
+ continue;
453
+ const target = tx?.to ?? receipt.contractAddress;
454
+ if (!target)
455
+ continue;
456
+ const to = ethers2.getAddress(target);
457
+ const selector = tx?.to ? (tx.input ?? "0x").slice(0, 10) : "(deployment)";
458
+ const key = `${to}:${selector}`;
459
+ const aggregate = aggregates.get(key) ?? { to, selector, calls: 0, totalGasUsed: 0n, totalMockGas: 0n };
460
+ aggregate.calls += 1;
461
+ aggregate.totalGasUsed += BigInt(receipt.gasUsed);
462
+ aggregate.totalMockGas += mockGas;
463
+ aggregates.set(key, aggregate);
464
+ } catch {
465
+ }
466
+ }
467
+ if (aggregates.size === 0)
468
+ return;
469
+ const selectorNames = await buildSelectorNameMap(hre);
470
+ const addressNames = await buildAddressNameMap(hre, [...new Set([...aggregates.values()].map((a) => a.to))]);
471
+ const merged = /* @__PURE__ */ new Map();
472
+ for (const a of aggregates.values()) {
473
+ const contract = addressNames.get(a.to) ?? shortAddress(a.to);
474
+ const method = selectorNames.get(a.selector) ?? a.selector;
475
+ const key = `${contract}:${method}`;
476
+ const row = merged.get(key) ?? { contract, method, calls: 0, totalGasUsed: 0n, totalMockGas: 0n };
477
+ row.calls += a.calls;
478
+ row.totalGasUsed += a.totalGasUsed;
479
+ row.totalMockGas += a.totalMockGas;
480
+ merged.set(key, row);
481
+ }
482
+ const rows = [...merged.values()].map((r) => {
483
+ const avgGasUsed = r.totalGasUsed / BigInt(r.calls);
484
+ const avgMockGas = r.totalMockGas / BigInt(r.calls);
485
+ return {
486
+ contract: r.contract,
487
+ method: r.method,
488
+ calls: r.calls,
489
+ avgGasUsed,
490
+ avgAdjusted: avgMockGas > avgGasUsed ? 0n : avgGasUsed - avgMockGas,
491
+ overheadPct: avgGasUsed === 0n ? 0 : Math.min(100, Number(avgMockGas * 100n / avgGasUsed))
492
+ };
493
+ }).sort((a, b) => a.contract.localeCompare(b.contract) || a.method.localeCompare(b.method));
494
+ const headers = ["Contract", "Method", "Calls", "Avg gas (mocks)", "Avg gas (adjusted)", "Mock overhead"];
495
+ const cells = rows.map((r) => [
496
+ r.contract,
497
+ r.method,
498
+ String(r.calls),
499
+ r.avgGasUsed.toLocaleString(),
500
+ r.avgAdjusted.toLocaleString(),
501
+ `${r.overheadPct}%`
502
+ ]);
503
+ const widths = headers.map((h, i) => Math.max(h.length, ...cells.map((row) => row[i].length)));
504
+ const line = (row) => `\u2502 ${row.map((cell, i) => cell.padEnd(widths[i])).join(" \u2502 ")} \u2502`;
505
+ const divider = (l, m, r) => `${l}${widths.map((w) => "\u2500".repeat(w + 2)).join(m)}${r}`;
506
+ console.log("");
507
+ console.log(chalk3.bold("[COFHE-MOCKS] Gas summary") + chalk3.dim(" \u2014 adjusted \u2248 cost excluding mock-only overhead"));
508
+ console.log(divider("\u250C", "\u252C", "\u2510"));
509
+ console.log(line(headers));
510
+ console.log(divider("\u251C", "\u253C", "\u2524"));
511
+ for (const row of cells)
512
+ console.log(line(row));
513
+ console.log(divider("\u2514", "\u2534", "\u2518"));
514
+ console.log(
515
+ chalk3.dim(
516
+ "Raw numbers include on-chain replication of off-chain CoFHE work; adjusted numbers estimate real-network cost. estimateGas remains unadjusted."
517
+ )
518
+ );
519
+ };
520
+
352
521
  // src/index.ts
353
522
  import { hardhat } from "@cofhe/sdk/chains";
354
523
  import {
355
524
  MockThresholdNetworkArtifact as MockThresholdNetworkArtifact2,
525
+ MockCoFHEAddressBookArtifact as MockCoFHEAddressBookArtifact2,
356
526
  MockTaskManagerArtifact as MockTaskManagerArtifact3,
357
527
  MockZkVerifierArtifact as MockZkVerifierArtifact2
358
528
  } from "@cofhe/mock-contracts";
@@ -393,23 +563,22 @@ subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(async (taskArgs, hre,
393
563
  return [.../* @__PURE__ */ new Set([...existing, stubPath])];
394
564
  });
395
565
  extendConfig((config, userConfig) => {
396
- if (userConfig.networks && userConfig.networks.localcofhe) {
397
- return;
566
+ if (!userConfig.networks?.localcofhe) {
567
+ config.networks.localcofhe = {
568
+ gas: "auto",
569
+ gasMultiplier: 1.2,
570
+ gasPrice: "auto",
571
+ timeout: 1e4,
572
+ httpHeaders: {},
573
+ url: "http://127.0.0.1:42069",
574
+ accounts: [
575
+ "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
576
+ "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
577
+ "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a",
578
+ "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6"
579
+ ]
580
+ };
398
581
  }
399
- config.networks.localcofhe = {
400
- gas: "auto",
401
- gasMultiplier: 1.2,
402
- gasPrice: "auto",
403
- timeout: 1e4,
404
- httpHeaders: {},
405
- url: "http://127.0.0.1:42069",
406
- accounts: [
407
- "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
408
- "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
409
- "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a",
410
- "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6"
411
- ]
412
- };
413
582
  if (!userConfig.networks?.["eth-sepolia"]) {
414
583
  config.networks["eth-sepolia"] = {
415
584
  url: process.env.SEPOLIA_RPC_URL ?? "https://ethereum-sepolia.publicnode.com",
@@ -437,6 +606,7 @@ extendConfig((config, userConfig) => {
437
606
  config.cofhe = {
438
607
  logMocks: userConfig.cofhe?.logMocks ?? false,
439
608
  gasWarning: userConfig.cofhe?.gasWarning ?? false,
609
+ gasSummary: userConfig.cofhe?.gasSummary ?? false,
440
610
  mocksDeployVerbosity: userConfig.cofhe?.mocksDeployVerbosity ?? "v"
441
611
  };
442
612
  });
@@ -444,18 +614,18 @@ task(TASK_COFHE_USE_FAUCET, "Fund an account from the funder").addOptionalParam(
444
614
  const { network } = hre;
445
615
  const { name: networkName } = network;
446
616
  if (networkName !== "localcofhe") {
447
- console.info(chalk3.yellow(`Programmatic faucet only supported for localcofhe`));
617
+ console.info(chalk4.yellow(`Programmatic faucet only supported for localcofhe`));
448
618
  return;
449
619
  }
450
620
  if (!address) {
451
- console.info(chalk3.red(`Failed to get address to fund`));
621
+ console.info(chalk4.red(`Failed to get address to fund`));
452
622
  return;
453
623
  }
454
- console.info(chalk3.green(`Getting funds from faucet for ${address}`));
624
+ console.info(chalk4.green(`Getting funds from faucet for ${address}`));
455
625
  try {
456
626
  await localcofheFundAccount(hre, address);
457
627
  } catch (e) {
458
- console.info(chalk3.red(`failed to get funds from localcofhe for ${address}: ${e}`));
628
+ console.info(chalk4.red(`failed to get funds from localcofhe for ${address}: ${e}`));
459
629
  }
460
630
  });
461
631
  task(TASK_COFHE_MOCKS_DEPLOY, "Deploys the mock contracts on the Hardhat network").setAction(
@@ -478,7 +648,11 @@ task(TASK_TEST, "Deploy mock contracts on hardhat").setAction(async ({}, hre, ru
478
648
  mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity
479
649
  });
480
650
  }
481
- return runSuper();
651
+ const result = await runSuper();
652
+ if (hre.config.cofhe.gasSummary) {
653
+ await printMockGasSummary(hre);
654
+ }
655
+ return result;
482
656
  });
483
657
  task(TASK_NODE, "Deploy mock contracts on hardhat").setAction(async ({}, hre, runSuper) => {
484
658
  const skipAutoDeploy = (() => {
@@ -544,6 +718,8 @@ extendEnvironment((hre) => {
544
718
  });
545
719
  return client;
546
720
  },
721
+ getAdjustedGasUsed: mock_getAdjustedGasUsed,
722
+ getAdjustedGasBreakdown: mock_getAdjustedGasBreakdown,
547
723
  mocks: {
548
724
  withLogs: async (closureName, closure) => {
549
725
  return mock_withLogs(hre, closureName, closure);
@@ -565,6 +741,7 @@ extendEnvironment((hre) => {
565
741
  const [signer] = await hre.ethers.getSigners();
566
742
  return mock_expectPlaintext(signer.provider, ctHash, expectedValue);
567
743
  },
744
+ getMockCoFHEAddressBook: async () => getFixedMockContract(hre, MockCoFHEAddressBookArtifact2),
568
745
  getMockTaskManager: async () => getFixedMockContract(hre, MockTaskManagerArtifact3),
569
746
  getMockACL: async () => {
570
747
  const taskManager = await getFixedMockContract(hre, MockTaskManagerArtifact3);
@@ -578,6 +755,7 @@ extendEnvironment((hre) => {
578
755
  };
579
756
  });
580
757
  export {
758
+ MOCK_GAS_CONSUMED_TOPIC,
581
759
  TASK_COFHE_MOCKS_DEPLOY,
582
760
  TASK_COFHE_MOCKS_SET_LOG_OPS,
583
761
  TASK_COFHE_USE_FAUCET,
@@ -588,9 +766,12 @@ export {
588
766
  localcofheFundAccount,
589
767
  localcofheFundWalletIfNeeded,
590
768
  mock_expectPlaintext,
769
+ mock_getAdjustedGasBreakdown,
770
+ mock_getAdjustedGasUsed,
591
771
  mock_getPlaintext,
592
772
  mock_getPlaintextExists,
593
773
  mock_setLoggingEnabled,
594
- mock_withLogs
774
+ mock_withLogs,
775
+ printMockGasSummary
595
776
  };
596
777
  //# sourceMappingURL=index.mjs.map