@awarizon/web3 1.0.0 → 1.0.2

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.js CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  var viem = require('viem');
4
4
  var walletEngine = require('@awarizon/wallet-engine');
5
+ var txEngine = require('@awarizon/tx-engine');
5
6
  var chains = require('viem/chains');
6
7
  var abiEngine = require('@awarizon/abi-engine');
7
- var txEngine = require('@awarizon/tx-engine');
8
8
 
9
9
  // src/sdk.ts
10
10
  var CHAINS = {
@@ -57,6 +57,7 @@ var CHAINS = {
57
57
  fantom: chains.fantom,
58
58
  moonbeam: chains.moonbeam
59
59
  };
60
+ var _chainAliases = Object.keys(CHAINS).sort().join(", ");
60
61
  function resolveChain(chain) {
61
62
  if (typeof chain === "object" && "id" in chain) {
62
63
  return chain;
@@ -64,10 +65,9 @@ function resolveChain(chain) {
64
65
  if (typeof chain === "string") {
65
66
  const resolved = CHAINS[chain.toLowerCase()] ?? CHAINS[chain];
66
67
  if (!resolved) {
67
- const available = Object.keys(CHAINS).filter((k, i, arr) => arr.indexOf(k) === i).sort().join(", ");
68
68
  throw new Error(
69
69
  `[awarizon/web3] Unsupported chain: "${chain}".
70
- Available chains: ${available}`
70
+ Available chains: ${_chainAliases}`
71
71
  );
72
72
  }
73
73
  return resolved;
@@ -82,37 +82,58 @@ function getSupportedChainIds() {
82
82
  return true;
83
83
  }).map((c) => c.id);
84
84
  }
85
- function buildContractInstance(address, abi, publicClient, getWalletClient) {
85
+ function trackEvent(telemetry, type, chain, functionName, success, start) {
86
+ telemetry?.track({
87
+ type,
88
+ chain: chain?.name ?? "unknown",
89
+ chainId: chain?.id ?? 0,
90
+ functionName,
91
+ success,
92
+ durationMs: Date.now() - start,
93
+ ts: Date.now()
94
+ });
95
+ }
96
+ function buildContractInstance(address, abi, publicClient, txEngine, telemetry, chain) {
86
97
  const parsed = abiEngine.parseABI(abi);
87
- const txEngine$1 = new txEngine.TransactionEngine(publicClient, getWalletClient);
98
+ const eventsMap = new Map(parsed.events.map((e) => [e.name, e]));
88
99
  const instance = {
89
100
  _address: address,
90
101
  _abi: abi
91
102
  };
92
103
  for (const fn of parsed.readFunctions) {
93
- instance[fn.name] = buildReadMethod(fn, address, abi, txEngine$1);
104
+ instance[fn.name] = buildReadMethod(fn, address, abi, txEngine, telemetry, chain);
94
105
  }
95
106
  for (const fn of parsed.writeFunctions) {
96
- instance[fn.name] = buildWriteMethod(fn, address, abi, txEngine$1);
107
+ instance[fn.name] = buildWriteMethod(fn, address, abi, txEngine, telemetry, chain);
97
108
  }
98
- instance["on"] = buildEventSubscription(address, abi, parsed.events, publicClient);
109
+ instance["on"] = buildEventSubscription(address, abi, eventsMap, publicClient);
99
110
  instance["estimateGas"] = async (method, ...args) => {
100
- const estimate = await txEngine$1.estimateGas({
101
- address,
102
- abi,
103
- functionName: method,
104
- args
105
- });
106
- return estimate.gas;
111
+ const start = Date.now();
112
+ try {
113
+ const estimate = await txEngine.estimateGas({ address, abi, functionName: method, args });
114
+ trackEvent(telemetry, "contract.gas_estimate", chain, method, true, start);
115
+ return estimate.gas;
116
+ } catch (error) {
117
+ trackEvent(telemetry, "contract.gas_estimate", chain, method, false, start);
118
+ throw error;
119
+ }
107
120
  };
108
121
  return instance;
109
122
  }
110
- function buildReadMethod(fn, address, abi, txEngine) {
123
+ function buildReadMethod(fn, address, abi, txEngine, telemetry, chain) {
111
124
  return async (...args) => {
112
- return txEngine.read({ address, abi, functionName: fn.name, args });
125
+ const start = Date.now();
126
+ try {
127
+ const result = await txEngine.read({ address, abi, functionName: fn.name, args });
128
+ trackEvent(telemetry, "contract.read", chain, fn.name, true, start);
129
+ return result;
130
+ } catch (error) {
131
+ trackEvent(telemetry, "contract.read", chain, fn.name, false, start);
132
+ throw error;
133
+ }
113
134
  };
114
135
  }
115
- function buildWriteMethod(fn, address, abi, txEngine) {
136
+ function buildWriteMethod(fn, address, abi, txEngine, telemetry, chain) {
116
137
  const isPayable = fn.stateMutability === "payable";
117
138
  return async (...args) => {
118
139
  let callArgs = args;
@@ -126,14 +147,22 @@ function buildWriteMethod(fn, address, abi, txEngine) {
126
147
  gas = last.gas;
127
148
  }
128
149
  }
129
- return txEngine.write({
130
- address,
131
- abi,
132
- functionName: fn.name,
133
- args: callArgs,
134
- value,
135
- gas
136
- });
150
+ const start = Date.now();
151
+ try {
152
+ const result = await txEngine.write({
153
+ address,
154
+ abi,
155
+ functionName: fn.name,
156
+ args: callArgs,
157
+ value,
158
+ gas
159
+ });
160
+ trackEvent(telemetry, "contract.write", chain, fn.name, true, start);
161
+ return result;
162
+ } catch (error) {
163
+ trackEvent(telemetry, "contract.write", chain, fn.name, false, start);
164
+ throw error;
165
+ }
137
166
  };
138
167
  }
139
168
  function isPayableOptions(val) {
@@ -143,11 +172,11 @@ function isPayableOptions(val) {
143
172
  const hasGas = "gas" in obj && (obj.gas === void 0 || typeof obj.gas === "bigint");
144
173
  return hasValue || hasGas;
145
174
  }
146
- function buildEventSubscription(address, abi, events, publicClient) {
175
+ function buildEventSubscription(address, abi, eventsMap, publicClient) {
147
176
  return (eventName, callback) => {
148
- const abiEvent = events.find((e) => e.name === eventName);
177
+ const abiEvent = eventsMap.get(eventName);
149
178
  if (!abiEvent) {
150
- const available = events.map((e) => e.name).join(", ") || "none";
179
+ const available = [...eventsMap.keys()].join(", ") || "none";
151
180
  throw new Error(
152
181
  `[awarizon/web3] Event "${eventName}" not found on contract ${address}.
153
182
  Available events: ${available}`
@@ -184,7 +213,7 @@ var ProviderError = class extends Error {
184
213
  };
185
214
  var ContractNotLoadedError = class extends Error {
186
215
  constructor(address) {
187
- super(`[awarizon/web3] Contract at ${address} is not loaded. Call sdk.contract({ address, abi }) first.`);
216
+ super(`[awarizon/web3] Contract at ${address} is not loaded. Call awarizon.contract({ address, abi }) first.`);
188
217
  this.code = "CONTRACT_NOT_LOADED";
189
218
  this.name = "ContractNotLoadedError";
190
219
  }
@@ -197,101 +226,431 @@ var UnsupportedChainError = class extends Error {
197
226
  this.chain = chain;
198
227
  }
199
228
  };
229
+ var ApiKeyRequiredError = class extends Error {
230
+ constructor() {
231
+ super(
232
+ '[awarizon/web3] An API key is required. Pass apiKey: "awz_live_..." to AwarizonWeb3({ ... }). Get yours at https://awarizon.com/dashboard/api-keys'
233
+ );
234
+ this.code = "API_KEY_REQUIRED";
235
+ this.name = "ApiKeyRequiredError";
236
+ }
237
+ };
238
+ var InvalidApiKeyError = class extends Error {
239
+ constructor(message = "Invalid or revoked API key.") {
240
+ super(`[awarizon/web3] ${message}`);
241
+ this.code = "INVALID_API_KEY";
242
+ this.name = "InvalidApiKeyError";
243
+ }
244
+ };
200
245
 
201
- // src/sdk.ts
246
+ // src/telemetry.ts
247
+ var DEFAULT_BASE_URL = "https://awarizon.com";
248
+ var FLUSH_INTERVAL_MS = 1e4;
249
+ var MAX_BATCH_SIZE = 20;
250
+ var VALIDATION_TIMEOUT = 5e3;
251
+ var TelemetryClient = class {
252
+ constructor(apiKey, baseUrl = DEFAULT_BASE_URL) {
253
+ this.validated = false;
254
+ this.validationPromise = null;
255
+ this.queue = [];
256
+ this.flushTimer = null;
257
+ this.unloadListener = null;
258
+ this.apiKey = apiKey;
259
+ this.baseUrl = baseUrl.replace(/\/$/, "");
260
+ }
261
+ /**
262
+ * Ensures the API key has been validated against the Awarizon API.
263
+ * Idempotent — safe to call on every operation; only hits the network once.
264
+ * Times out after 5 seconds to avoid hanging in degraded network conditions.
265
+ *
266
+ * @throws {InvalidApiKeyError} if the key is invalid, revoked, or unreachable
267
+ */
268
+ async ensureValidated() {
269
+ if (this.validated) return;
270
+ if (this.validationPromise) return this.validationPromise;
271
+ this.validationPromise = this._validate().catch((err) => {
272
+ this.validationPromise = null;
273
+ throw err;
274
+ });
275
+ return this.validationPromise;
276
+ }
277
+ /**
278
+ * Queue a usage event. Events are batched and sent periodically.
279
+ * Must only be called after ensureValidated() has resolved.
280
+ */
281
+ track(event) {
282
+ if (!this.validated) return;
283
+ this.queue.push(event);
284
+ if (this.queue.length >= MAX_BATCH_SIZE) this._flush(false);
285
+ }
286
+ /**
287
+ * Flush pending events and stop background timers.
288
+ * Call when tearing down a long-lived SDK instance (e.g. server shutdown).
289
+ */
290
+ async destroy() {
291
+ this._stopTimers();
292
+ await this._flush(true);
293
+ }
294
+ // ─── Private ─────────────────────────────────────────────────────────────────
295
+ async _validate() {
296
+ const controller = new AbortController();
297
+ const timeout = setTimeout(() => controller.abort(), VALIDATION_TIMEOUT);
298
+ let res;
299
+ try {
300
+ res = await fetch(`${this.baseUrl}/api/v1/auth`, {
301
+ method: "GET",
302
+ headers: { Authorization: `Bearer ${this.apiKey}` },
303
+ signal: controller.signal
304
+ });
305
+ } catch (err) {
306
+ const msg = controller.signal.aborted ? `Awarizon API did not respond within ${VALIDATION_TIMEOUT / 1e3}s.` : `Could not reach Awarizon API: ${err.message}`;
307
+ throw new InvalidApiKeyError(msg);
308
+ } finally {
309
+ clearTimeout(timeout);
310
+ }
311
+ if (!res.ok) {
312
+ const body = await res.json().catch(() => ({}));
313
+ throw new InvalidApiKeyError(body.error ?? "Invalid or revoked API key.");
314
+ }
315
+ this.validated = true;
316
+ this._startTimers();
317
+ }
318
+ _startTimers() {
319
+ this.flushTimer = setInterval(() => {
320
+ if (this.queue.length > 0) this._flush(false);
321
+ }, FLUSH_INTERVAL_MS);
322
+ const timer = this.flushTimer;
323
+ if (typeof timer.unref === "function") timer.unref();
324
+ if (typeof window !== "undefined" && typeof navigator !== "undefined") {
325
+ this.unloadListener = () => this._flushBeacon();
326
+ window.addEventListener("beforeunload", this.unloadListener);
327
+ }
328
+ }
329
+ _stopTimers() {
330
+ if (this.flushTimer !== null) {
331
+ clearInterval(this.flushTimer);
332
+ this.flushTimer = null;
333
+ }
334
+ if (this.unloadListener !== null && typeof window !== "undefined") {
335
+ window.removeEventListener("beforeunload", this.unloadListener);
336
+ this.unloadListener = null;
337
+ }
338
+ }
339
+ _flush(awaited) {
340
+ if (this.queue.length === 0) return awaited ? Promise.resolve() : void 0;
341
+ const batch = this.queue.splice(0, MAX_BATCH_SIZE);
342
+ const promise = fetch(`${this.baseUrl}/api/v1/usage`, {
343
+ method: "POST",
344
+ headers: {
345
+ Authorization: `Bearer ${this.apiKey}`,
346
+ "Content-Type": "application/json"
347
+ },
348
+ body: JSON.stringify({ events: batch })
349
+ }).then(() => {
350
+ }, () => {
351
+ });
352
+ return awaited ? promise : void 0;
353
+ }
354
+ /** Uses sendBeacon for guaranteed delivery on page unload */
355
+ _flushBeacon() {
356
+ if (this.queue.length === 0 || typeof navigator === "undefined") return;
357
+ const batch = this.queue.splice(0);
358
+ navigator.sendBeacon(
359
+ `${this.baseUrl}/api/v1/usage`,
360
+ new Blob([JSON.stringify({ events: batch })], { type: "application/json" })
361
+ );
362
+ }
363
+ };
364
+ var WalletProxy = class {
365
+ constructor(engine, ensureReady) {
366
+ this.engine = engine;
367
+ this.ensureReady = ensureReady;
368
+ }
369
+ // ── Gated async operations ──────────────────────────────────────────────────
370
+ async create() {
371
+ await this.ensureReady();
372
+ return this.engine.create();
373
+ }
374
+ async importMnemonic(mnemonic, accountIndex) {
375
+ await this.ensureReady();
376
+ return this.engine.importMnemonic(mnemonic, accountIndex);
377
+ }
378
+ async importPrivateKey(privateKey) {
379
+ await this.ensureReady();
380
+ return this.engine.importPrivateKey(privateKey);
381
+ }
382
+ // ── Pass-throughs (sync, no network) ───────────────────────────────────────
383
+ address() {
384
+ return this.engine.address();
385
+ }
386
+ getSigner() {
387
+ return this.engine.getWalletClient();
388
+ }
389
+ getWalletClient() {
390
+ return this.engine.getWalletClient();
391
+ }
392
+ isConnected() {
393
+ return this.engine.isConnected();
394
+ }
395
+ hasExternalWallet() {
396
+ return this.engine.hasExternalWallet();
397
+ }
398
+ hasInternalWallet() {
399
+ return this.engine.hasInternalWallet();
400
+ }
401
+ connectExternal(c) {
402
+ this.engine.connectExternal(c);
403
+ }
404
+ disconnectExternal() {
405
+ this.engine.disconnectExternal();
406
+ }
407
+ disconnect() {
408
+ this.engine.disconnect();
409
+ }
410
+ async switchChain(chain) {
411
+ return this.engine.switchChain(chain);
412
+ }
413
+ };
202
414
  var AwarizonWeb3 = class {
203
415
  constructor(config) {
416
+ // Contract instances are cached by address — same address returns same instance.
417
+ // Cache is cleared on switchChain() since contracts are chain-specific.
418
+ this._contractCache = /* @__PURE__ */ new Map();
419
+ if (!config.apiKey) throw new ApiKeyRequiredError();
204
420
  this.chain = resolveChain(config.chain);
205
421
  this.publicClient = viem.createPublicClient({
206
422
  chain: this.chain,
207
423
  transport: viem.http(config.rpcUrl)
208
424
  });
209
- this.wallet = new walletEngine.WalletEngine({
425
+ this._engine = new walletEngine.WalletEngine({
210
426
  chain: this.chain,
211
427
  publicClient: this.publicClient,
212
428
  rpcUrl: config.rpcUrl
213
429
  });
430
+ this._txEngine = new txEngine.TransactionEngine(
431
+ this.publicClient,
432
+ () => this._engine.getWalletClient()
433
+ );
434
+ this._telemetry = new TelemetryClient(config.apiKey, config.baseUrl);
435
+ this.wallet = new WalletProxy(
436
+ this._engine,
437
+ () => this._telemetry.ensureValidated()
438
+ );
214
439
  if (config.signer) {
215
- this.wallet.connectExternal(config.signer);
440
+ this._engine.connectExternal(config.signer);
216
441
  }
217
442
  }
218
443
  // ─── Wallet API surface ─────────────────────────────────────────────────────
219
- /**
220
- * Connect an external wallet client (wagmi, WalletConnect, viem, EIP-1193).
221
- * The connected signer takes priority over any internal wallet.
222
- *
223
- * @example
224
- * sdk.connectWallet(walletClient)
225
- */
226
444
  connectWallet(walletClient) {
227
- this.wallet.connectExternal(walletClient);
445
+ this._engine.connectExternal(walletClient);
228
446
  return this;
229
447
  }
230
- /**
231
- * Disconnect the externally-connected wallet.
232
- * Falls back to the internal wallet if one is loaded.
233
- */
234
448
  disconnectWallet() {
235
- this.wallet.disconnectExternal();
449
+ this._engine.disconnectExternal();
236
450
  return this;
237
451
  }
238
- /**
239
- * Switch the active chain.
240
- * Rebuilds the PublicClient and internal WalletClient for the new chain.
241
- */
242
452
  async switchChain(chain) {
453
+ await this._telemetry.ensureValidated();
243
454
  const resolved = resolveChain(chain);
244
- await this.wallet.switchChain(resolved);
455
+ await this._engine.switchChain(resolved);
456
+ this._contractCache.clear();
245
457
  return this;
246
458
  }
247
- /**
248
- * Returns a summary of the currently active wallet.
249
- * @throws {WalletNotConnectedError} if no wallet is connected
250
- */
251
459
  getWalletInfo() {
252
460
  return {
253
- address: this.wallet.address(),
461
+ address: this._engine.address(),
254
462
  chain: this.chain,
255
- isExternal: this.wallet.hasExternalWallet()
463
+ isExternal: this._engine.hasExternalWallet()
256
464
  };
257
465
  }
258
466
  // ─── Contract API ───────────────────────────────────────────────────────────
259
467
  /**
260
- * Load a deployed smart contract and return a fully typed, ABI-powered
261
- * instance. Every function in the ABI is exposed as a direct method.
262
- *
263
- * **Read functions** (view / pure) use the PublicClient — no signer needed.
264
- * **Write functions** (nonpayable / payable) use the active WalletClient.
468
+ * Load a deployed contract and return a fully typed, ABI-powered instance.
469
+ * Validates the API key on the first call subsequent calls are instant.
265
470
  *
266
- * @example
267
- * const token = await sdk.contract({ address: "0x...", abi: ERC20_ABI })
268
- * const balance = await token.balanceOf(address) // read
269
- * await token.transfer(to, 100n) // write
270
- * token.on("Transfer", handler) // events
471
+ * Results are cached by address — calling contract() twice with the same
472
+ * address and ABI object returns the same instance without rebuilding.
271
473
  */
272
474
  async contract(config) {
475
+ await this._telemetry.ensureValidated();
273
476
  if (!config.address || !config.abi) {
274
- throw new Error("[awarizon/web3] sdk.contract() requires both address and abi");
477
+ throw new Error("[awarizon/web3] awarizon.contract() requires both address and abi");
275
478
  }
276
- return buildContractInstance(
479
+ const key = config.address.toLowerCase();
480
+ const cached = this._contractCache.get(key);
481
+ if (cached && cached.abiRef === config.abi) return cached.instance;
482
+ const instance = buildContractInstance(
277
483
  config.address,
278
484
  config.abi,
279
485
  this.publicClient,
280
- () => this.wallet.getWalletClient()
486
+ this._txEngine,
487
+ this._telemetry,
488
+ this.chain
281
489
  );
490
+ this._contractCache.set(key, { abiRef: config.abi, instance });
491
+ return instance;
492
+ }
493
+ /**
494
+ * Load multiple contracts in parallel. Validates the API key once, then
495
+ * instantiates all contracts concurrently.
496
+ *
497
+ * @example
498
+ * ```ts
499
+ * const [token, staking, nft] = await awarizon.contracts([
500
+ * { address: tokenAddr, abi: ERC20_ABI },
501
+ * { address: stakingAddr, abi: STAKING_ABI },
502
+ * { address: nftAddr, abi: ERC721_ABI },
503
+ * ])
504
+ * ```
505
+ */
506
+ async contracts(configs) {
507
+ return Promise.all(configs.map((c) => this.contract(c)));
282
508
  }
283
509
  /**
284
- * Returns the chain ID of the current chain.
285
- * Useful for runtime network validation.
510
+ * Call a read-only (view/pure) contract function directly without loading
511
+ * a full contract instance. Best for one-off reads.
512
+ *
513
+ * @example
514
+ * ```ts
515
+ * const balance = await awarizon.read<bigint>({
516
+ * address: tokenAddress,
517
+ * abi: ERC20_ABI,
518
+ * method: "balanceOf",
519
+ * args: [userAddress],
520
+ * })
521
+ * ```
286
522
  */
523
+ async read(params) {
524
+ await this._telemetry.ensureValidated();
525
+ const start = Date.now();
526
+ try {
527
+ const result = await this._txEngine.read({
528
+ address: params.address,
529
+ abi: params.abi,
530
+ functionName: params.method,
531
+ args: params.args ?? []
532
+ });
533
+ this._telemetry.track({
534
+ type: "contract.read",
535
+ chain: this.chain.name,
536
+ chainId: this.chain.id,
537
+ functionName: params.method,
538
+ success: true,
539
+ durationMs: Date.now() - start,
540
+ ts: Date.now()
541
+ });
542
+ return result;
543
+ } catch (err) {
544
+ this._telemetry.track({
545
+ type: "contract.read",
546
+ chain: this.chain.name,
547
+ chainId: this.chain.id,
548
+ functionName: params.method,
549
+ success: false,
550
+ durationMs: Date.now() - start,
551
+ ts: Date.now()
552
+ });
553
+ throw err;
554
+ }
555
+ }
556
+ /**
557
+ * Execute a state-mutating contract function directly without loading a full
558
+ * contract instance. Best for one-off writes.
559
+ *
560
+ * @example
561
+ * ```ts
562
+ * const { hash, receipt } = await awarizon.write({
563
+ * address: tokenAddress,
564
+ * abi: ERC20_ABI,
565
+ * method: "transfer",
566
+ * args: [recipient, 100n],
567
+ * })
568
+ * ```
569
+ */
570
+ async write(params) {
571
+ await this._telemetry.ensureValidated();
572
+ const start = Date.now();
573
+ try {
574
+ const result = await this._txEngine.write({
575
+ address: params.address,
576
+ abi: params.abi,
577
+ functionName: params.method,
578
+ args: params.args ?? [],
579
+ value: params.value,
580
+ gas: params.gas
581
+ });
582
+ this._telemetry.track({
583
+ type: "contract.write",
584
+ chain: this.chain.name,
585
+ chainId: this.chain.id,
586
+ functionName: params.method,
587
+ success: true,
588
+ durationMs: Date.now() - start,
589
+ ts: Date.now()
590
+ });
591
+ return result;
592
+ } catch (err) {
593
+ this._telemetry.track({
594
+ type: "contract.write",
595
+ chain: this.chain.name,
596
+ chainId: this.chain.id,
597
+ functionName: params.method,
598
+ success: false,
599
+ durationMs: Date.now() - start,
600
+ ts: Date.now()
601
+ });
602
+ throw err;
603
+ }
604
+ }
605
+ /**
606
+ * Batch multiple read calls into a single RPC round-trip using multicall3.
607
+ * Falls back to parallel individual reads on chains where multicall3 is
608
+ * not deployed.
609
+ *
610
+ * @example
611
+ * ```ts
612
+ * const [balance, supply, allowance] = await awarizon.multicall([
613
+ * { address: tokenAddr, abi: ERC20_ABI, method: "balanceOf", args: [userAddr] },
614
+ * { address: tokenAddr, abi: ERC20_ABI, method: "totalSupply" },
615
+ * { address: tokenAddr, abi: ERC20_ABI, method: "allowance", args: [userAddr, spenderAddr] },
616
+ * ])
617
+ * ```
618
+ */
619
+ async multicall(calls) {
620
+ await this._telemetry.ensureValidated();
621
+ const contracts = calls.map((c) => ({
622
+ address: c.address,
623
+ abi: c.abi,
624
+ functionName: c.method,
625
+ args: c.args ?? []
626
+ }));
627
+ try {
628
+ const results = await this.publicClient.multicall({
629
+ contracts,
630
+ allowFailure: false
631
+ });
632
+ return results;
633
+ } catch {
634
+ return Promise.all(
635
+ contracts.map(
636
+ (c) => this.publicClient.readContract(c)
637
+ )
638
+ );
639
+ }
640
+ }
641
+ // ─── Accessors ──────────────────────────────────────────────────────────────
287
642
  get chainId() {
288
643
  return this.chain.id;
289
644
  }
645
+ get isConnected() {
646
+ return this._engine.isConnected();
647
+ }
290
648
  /**
291
- * Check whether any wallet (internal or external) is connected.
649
+ * Flush pending telemetry and stop background timers.
650
+ * Call when tearing down a long-lived SDK instance (e.g. on server shutdown).
292
651
  */
293
- get isConnected() {
294
- return this.wallet.isConnected();
652
+ async destroy() {
653
+ await this._telemetry.destroy();
295
654
  }
296
655
  };
297
656
 
@@ -315,6 +674,26 @@ Object.defineProperty(exports, "WalletNotConnectedError", {
315
674
  enumerable: true,
316
675
  get: function () { return walletEngine.WalletNotConnectedError; }
317
676
  });
677
+ Object.defineProperty(exports, "ContractExecutionError", {
678
+ enumerable: true,
679
+ get: function () { return txEngine.ContractExecutionError; }
680
+ });
681
+ Object.defineProperty(exports, "GasEstimationError", {
682
+ enumerable: true,
683
+ get: function () { return txEngine.GasEstimationError; }
684
+ });
685
+ Object.defineProperty(exports, "SimulationError", {
686
+ enumerable: true,
687
+ get: function () { return txEngine.SimulationError; }
688
+ });
689
+ Object.defineProperty(exports, "TransactionEngine", {
690
+ enumerable: true,
691
+ get: function () { return txEngine.TransactionEngine; }
692
+ });
693
+ Object.defineProperty(exports, "TransactionTimeoutError", {
694
+ enumerable: true,
695
+ get: function () { return txEngine.TransactionTimeoutError; }
696
+ });
318
697
  Object.defineProperty(exports, "DuplicateFunctionError", {
319
698
  enumerable: true,
320
699
  get: function () { return abiEngine.DuplicateFunctionError; }
@@ -347,31 +726,14 @@ Object.defineProperty(exports, "parseABI", {
347
726
  enumerable: true,
348
727
  get: function () { return abiEngine.parseABI; }
349
728
  });
350
- Object.defineProperty(exports, "ContractExecutionError", {
351
- enumerable: true,
352
- get: function () { return txEngine.ContractExecutionError; }
353
- });
354
- Object.defineProperty(exports, "GasEstimationError", {
355
- enumerable: true,
356
- get: function () { return txEngine.GasEstimationError; }
357
- });
358
- Object.defineProperty(exports, "SimulationError", {
359
- enumerable: true,
360
- get: function () { return txEngine.SimulationError; }
361
- });
362
- Object.defineProperty(exports, "TransactionEngine", {
363
- enumerable: true,
364
- get: function () { return txEngine.TransactionEngine; }
365
- });
366
- Object.defineProperty(exports, "TransactionTimeoutError", {
367
- enumerable: true,
368
- get: function () { return txEngine.TransactionTimeoutError; }
369
- });
729
+ exports.ApiKeyRequiredError = ApiKeyRequiredError;
370
730
  exports.AwarizonWeb3 = AwarizonWeb3;
371
731
  exports.CHAINS = CHAINS;
372
732
  exports.ContractNotLoadedError = ContractNotLoadedError;
733
+ exports.InvalidApiKeyError = InvalidApiKeyError;
373
734
  exports.NetworkMismatchError = NetworkMismatchError;
374
735
  exports.ProviderError = ProviderError;
736
+ exports.TelemetryClient = TelemetryClient;
375
737
  exports.UnsupportedChainError = UnsupportedChainError;
376
738
  exports.getSupportedChainIds = getSupportedChainIds;
377
739
  exports.resolveChain = resolveChain;