clawntenna 0.9.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -21,7 +21,8 @@ var CHAINS = {
21
21
  keyManager: "0x5562B553a876CBdc8AA4B3fb0687f22760F4759e",
22
22
  schemaRegistry: "0xB7eB50e9058198b99b5b2589E6D70b2d99d5440a",
23
23
  identityRegistry: "0x8004AA63c570c570eBF15376c0dB199918BFe9Fb",
24
- escrow: "0x74e376C53f4afd5Cd32a77dDc627f477FcFC2333"
24
+ escrow: "0x74e376C53f4afd5Cd32a77dDc627f477FcFC2333",
25
+ defaultLookback: 2e5
25
26
  },
26
27
  base: {
27
28
  chainId: 8453,
@@ -32,7 +33,9 @@ var CHAINS = {
32
33
  registry: "0x5fF6BF04F1B5A78ae884D977a3C80A0D8E2072bF",
33
34
  keyManager: "0xdc302ff43a34F6aEa19426D60C9D150e0661E4f4",
34
35
  schemaRegistry: "0x5c11d2eA4470eD9025D810A21a885FE16dC987Bd",
35
- identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
36
+ identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
37
+ escrow: "0x04eC9a25C942192834F447eC9192831B56Ae2D7D",
38
+ defaultLookback: 2e5
36
39
  },
37
40
  avalanche: {
38
41
  chainId: 43114,
@@ -43,7 +46,9 @@ var CHAINS = {
43
46
  registry: "0x3Ca2FF0bD1b3633513299EB5d3e2d63e058b0713",
44
47
  keyManager: "0x5a5ea9D408FBA984fFf6e243Dcc71ff6E00C73E4",
45
48
  schemaRegistry: "0x23D96e610E8E3DA5341a75B77F1BFF7EA9c3A62B",
46
- identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
49
+ identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
50
+ escrow: "0x4068245c35a498Da4336aD1Ab0Fb71ef534bfd03",
51
+ defaultLookback: 5e5
47
52
  }
48
53
  };
49
54
 
@@ -3355,16 +3360,93 @@ function getDepositDeadline(depositedAt, timeout) {
3355
3360
  return Number(depositedAt + timeout);
3356
3361
  }
3357
3362
 
3363
+ // src/rpc-errors.ts
3364
+ function classifyRpcError(err, ctx) {
3365
+ const msg = err.message ?? "";
3366
+ if (msg.includes("BAD_DATA") || msg.includes("could not decode result data")) {
3367
+ return `${ctx.method} failed: contract may not be deployed on ${ctx.chainName}, or the RPC returned an empty response. Check that the correct chain and RPC URL are configured.`;
3368
+ }
3369
+ if (msg.includes("NETWORK_ERROR") || msg.includes("ECONNREFUSED") || msg.includes("fetch failed") || msg.includes("getaddrinfo")) {
3370
+ return `${ctx.method} failed: network error connecting to ${ctx.chainName} RPC. Check your RPC URL and network connectivity.`;
3371
+ }
3372
+ if (msg.includes("429") || msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("exceeded") || msg.includes("throttl")) {
3373
+ return `${ctx.method} failed: RPC rate limit hit on ${ctx.chainName}. The request was retried but the limit persists. Try again later or use a different RPC endpoint.`;
3374
+ }
3375
+ return null;
3376
+ }
3377
+
3378
+ // src/retry.ts
3379
+ var DEFAULT_RETRY = {
3380
+ maxRetries: 3,
3381
+ baseDelayMs: 1e3,
3382
+ maxDelayMs: 1e4
3383
+ };
3384
+ var RETRYABLE_PATTERNS = [
3385
+ "429",
3386
+ "rate limit",
3387
+ "too many requests",
3388
+ "timeout",
3389
+ "econnreset",
3390
+ "502",
3391
+ "503",
3392
+ "504",
3393
+ "server error"
3394
+ ];
3395
+ function isRetryableError(err) {
3396
+ const msg = (err.message ?? "").toLowerCase();
3397
+ return RETRYABLE_PATTERNS.some((p) => msg.includes(p));
3398
+ }
3399
+ async function withRetry(fn, options) {
3400
+ const opts = { ...DEFAULT_RETRY, ...options };
3401
+ let lastError;
3402
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
3403
+ try {
3404
+ return await fn();
3405
+ } catch (err) {
3406
+ lastError = err;
3407
+ if (attempt === opts.maxRetries) break;
3408
+ if (!(err instanceof Error) || !isRetryableError(err)) break;
3409
+ const delay = Math.min(opts.baseDelayMs * 2 ** attempt, opts.maxDelayMs);
3410
+ const jitter = Math.random() * 0.25 * delay;
3411
+ await new Promise((r) => setTimeout(r, delay + jitter));
3412
+ }
3413
+ }
3414
+ throw lastError;
3415
+ }
3416
+
3358
3417
  // src/client.ts
3359
3418
  var Clawntenna = class _Clawntenna {
3360
3419
  provider;
3361
- wallet;
3362
- registry;
3363
- keyManager;
3364
- schemaRegistry;
3365
- identityRegistry;
3366
- escrow;
3367
3420
  chainName;
3421
+ _signer;
3422
+ _address;
3423
+ _registry;
3424
+ _keyManager;
3425
+ _schemaRegistry;
3426
+ _identityRegistry;
3427
+ _escrow;
3428
+ /** @deprecated Use `signer` instead. */
3429
+ get wallet() {
3430
+ return this._signer;
3431
+ }
3432
+ get signer() {
3433
+ return this._signer;
3434
+ }
3435
+ get registry() {
3436
+ return this._registry;
3437
+ }
3438
+ get keyManager() {
3439
+ return this._keyManager;
3440
+ }
3441
+ get schemaRegistry() {
3442
+ return this._schemaRegistry;
3443
+ }
3444
+ get identityRegistry() {
3445
+ return this._identityRegistry;
3446
+ }
3447
+ get escrow() {
3448
+ return this._escrow;
3449
+ }
3368
3450
  // In-memory ECDH state
3369
3451
  ecdhPrivateKey = null;
3370
3452
  ecdhPublicKey = null;
@@ -3383,25 +3465,59 @@ var Clawntenna = class _Clawntenna {
3383
3465
  const keyManagerAddr = options.keyManagerAddress ?? chain.keyManager;
3384
3466
  const schemaRegistryAddr = options.schemaRegistryAddress ?? chain.schemaRegistry;
3385
3467
  const escrowAddr = options.escrowAddress ?? chain.escrow;
3386
- const signer = options.privateKey ? new ethers.Wallet(options.privateKey, this.provider) : null;
3468
+ const wallet = options.privateKey ? new ethers.Wallet(options.privateKey, this.provider) : null;
3469
+ const signer = wallet ? new ethers.NonceManager(wallet) : null;
3387
3470
  const runner = signer ?? this.provider;
3388
- if (signer) {
3389
- this.wallet = signer;
3390
- this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, signer);
3391
- this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, signer);
3392
- this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, signer);
3393
- this.identityRegistry = chain.identityRegistry ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, signer) : null;
3394
- } else {
3395
- this.wallet = null;
3396
- this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.provider);
3397
- this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.provider);
3398
- this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.provider);
3399
- this.identityRegistry = chain.identityRegistry ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.provider) : null;
3471
+ this._signer = signer;
3472
+ this._address = wallet?.address ?? null;
3473
+ this._registry = new ethers.Contract(registryAddr, REGISTRY_ABI, runner);
3474
+ this._keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, runner);
3475
+ this._schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, runner);
3476
+ this._identityRegistry = chain.identityRegistry ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, runner) : null;
3477
+ this._escrow = escrowAddr ? new ethers.Contract(escrowAddr, ESCROW_ABI, runner) : null;
3478
+ }
3479
+ /**
3480
+ * Connect an external signer (e.g. from BrowserProvider).
3481
+ * Reconnects all contract instances to the new signer.
3482
+ */
3483
+ async connectSigner(signer) {
3484
+ this._address = await signer.getAddress();
3485
+ this._signer = signer;
3486
+ this._registry = this._registry.connect(signer);
3487
+ this._keyManager = this._keyManager.connect(signer);
3488
+ this._schemaRegistry = this._schemaRegistry.connect(signer);
3489
+ if (this._identityRegistry) {
3490
+ this._identityRegistry = this._identityRegistry.connect(signer);
3491
+ }
3492
+ if (this._escrow) {
3493
+ this._escrow = this._escrow.connect(signer);
3494
+ }
3495
+ }
3496
+ requireSigner() {
3497
+ if (!this._signer) {
3498
+ throw new Error("Signer required. Pass privateKey in constructor or call connectSigner().");
3499
+ }
3500
+ return this._signer;
3501
+ }
3502
+ requireAddress() {
3503
+ if (!this._address) {
3504
+ throw new Error("Signer required. Pass privateKey in constructor or call connectSigner().");
3505
+ }
3506
+ return this._address;
3507
+ }
3508
+ async _wrapRpcError(fn, method) {
3509
+ try {
3510
+ return await withRetry(fn);
3511
+ } catch (err) {
3512
+ if (err instanceof Error) {
3513
+ const hint = classifyRpcError(err, { method, chainName: this.chainName });
3514
+ if (hint) throw new Error(hint, { cause: err });
3515
+ }
3516
+ throw err;
3400
3517
  }
3401
- this.escrow = escrowAddr ? new ethers.Contract(escrowAddr, ESCROW_ABI, runner) : null;
3402
3518
  }
3403
3519
  get address() {
3404
- return this.wallet?.address ?? null;
3520
+ return this._address;
3405
3521
  }
3406
3522
  // ===== MESSAGING =====
3407
3523
  /**
@@ -3409,7 +3525,7 @@ var Clawntenna = class _Clawntenna {
3409
3525
  * Automatically determines encryption key based on topic access level.
3410
3526
  */
3411
3527
  async sendMessage(topicId, text, options) {
3412
- if (!this.wallet) throw new Error("Wallet required to send messages");
3528
+ this.requireSigner();
3413
3529
  if (options?.replyTo && this.escrow && !options?.skipRefundCheck) {
3414
3530
  const refunded = await this.isMessageRefunded(options.replyTo);
3415
3531
  if (refunded) {
@@ -3447,13 +3563,17 @@ var Clawntenna = class _Clawntenna {
3447
3563
  const filter = this.registry.filters.MessageSent(topicId);
3448
3564
  const CHUNK_SIZE = 2e3;
3449
3565
  const currentBlock = await this.provider.getBlockNumber();
3450
- const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : 1e5;
3566
+ const chain = CHAINS[this.chainName];
3567
+ const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : chain.defaultLookback;
3451
3568
  const startBlock = currentBlock - maxRange;
3452
3569
  const allEvents = [];
3453
3570
  let toBlock = currentBlock;
3454
3571
  while (toBlock > startBlock && allEvents.length < limit) {
3455
3572
  const chunkFrom = Math.max(toBlock - CHUNK_SIZE + 1, startBlock);
3456
- const events = await this.registry.queryFilter(filter, chunkFrom, toBlock);
3573
+ const events = await this._wrapRpcError(
3574
+ () => this.registry.queryFilter(filter, chunkFrom, toBlock),
3575
+ "readMessages"
3576
+ );
3457
3577
  allEvents.unshift(...events);
3458
3578
  toBlock = chunkFrom - 1;
3459
3579
  }
@@ -3463,12 +3583,12 @@ var Clawntenna = class _Clawntenna {
3463
3583
  const payloadStr = ethers.toUtf8String(log.args.payload);
3464
3584
  const parsed = decryptMessage(payloadStr, key);
3465
3585
  messages.push({
3466
- topicId: BigInt(topicId),
3586
+ topicId,
3467
3587
  sender: log.args.sender,
3468
3588
  text: parsed?.text ?? "[decryption failed]",
3469
3589
  replyTo: parsed?.replyTo ?? null,
3470
3590
  mentions: parsed?.mentions ?? null,
3471
- timestamp: log.args.timestamp,
3591
+ timestamp: Number(log.args.timestamp),
3472
3592
  txHash: log.transactionHash,
3473
3593
  blockNumber: log.blockNumber
3474
3594
  });
@@ -3489,12 +3609,12 @@ var Clawntenna = class _Clawntenna {
3489
3609
  const payloadStr = ethers.toUtf8String(payload);
3490
3610
  const parsed = decryptMessage(payloadStr, key);
3491
3611
  callback({
3492
- topicId: tId,
3612
+ topicId: Number(tId),
3493
3613
  sender,
3494
3614
  text: parsed?.text ?? "[decryption failed]",
3495
3615
  replyTo: parsed?.replyTo ?? null,
3496
3616
  mentions: parsed?.mentions ?? null,
3497
- timestamp,
3617
+ timestamp: Number(timestamp),
3498
3618
  txHash: event.transactionHash,
3499
3619
  blockNumber: event.blockNumber
3500
3620
  });
@@ -3506,7 +3626,7 @@ var Clawntenna = class _Clawntenna {
3506
3626
  }
3507
3627
  // ===== NICKNAMES =====
3508
3628
  async setNickname(appId, nickname) {
3509
- if (!this.wallet) throw new Error("Wallet required");
3629
+ this.requireSigner();
3510
3630
  return this.registry.setNickname(appId, nickname);
3511
3631
  }
3512
3632
  async getNickname(appId, address) {
@@ -3520,11 +3640,11 @@ var Clawntenna = class _Clawntenna {
3520
3640
  return { canChange, timeRemaining };
3521
3641
  }
3522
3642
  async clearNickname(appId) {
3523
- if (!this.wallet) throw new Error("Wallet required");
3643
+ this.requireSigner();
3524
3644
  return this.registry.clearNickname(appId);
3525
3645
  }
3526
3646
  async setNicknameCooldown(appId, cooldownSeconds) {
3527
- if (!this.wallet) throw new Error("Wallet required");
3647
+ this.requireSigner();
3528
3648
  return this.registry.setNicknameCooldown(appId, cooldownSeconds);
3529
3649
  }
3530
3650
  async getNicknameCooldown(appId) {
@@ -3532,24 +3652,26 @@ var Clawntenna = class _Clawntenna {
3532
3652
  }
3533
3653
  // ===== TOPICS =====
3534
3654
  async createTopic(appId, name, description, accessLevel) {
3535
- if (!this.wallet) throw new Error("Wallet required");
3655
+ this.requireSigner();
3536
3656
  return this.registry.createTopic(appId, name, description, accessLevel);
3537
3657
  }
3538
3658
  async getTopic(topicId) {
3539
- const t = await this.registry.getTopic(topicId);
3540
- return {
3541
- id: t.id,
3542
- applicationId: t.applicationId,
3543
- name: t.name,
3544
- description: t.description,
3545
- owner: t.owner,
3546
- creator: t.creator,
3547
- createdAt: t.createdAt,
3548
- lastMessageAt: t.lastMessageAt,
3549
- messageCount: t.messageCount,
3550
- accessLevel: Number(t.accessLevel),
3551
- active: t.active
3552
- };
3659
+ return this._wrapRpcError(async () => {
3660
+ const t = await this.registry.getTopic(topicId);
3661
+ return {
3662
+ id: t.id,
3663
+ applicationId: t.applicationId,
3664
+ name: t.name,
3665
+ description: t.description,
3666
+ owner: t.owner,
3667
+ creator: t.creator,
3668
+ createdAt: t.createdAt,
3669
+ lastMessageAt: t.lastMessageAt,
3670
+ messageCount: t.messageCount,
3671
+ accessLevel: Number(t.accessLevel),
3672
+ active: t.active
3673
+ };
3674
+ }, "getTopic");
3553
3675
  }
3554
3676
  async getApplicationTopics(appId) {
3555
3677
  return this.registry.getApplicationTopics(appId);
@@ -3559,7 +3681,7 @@ var Clawntenna = class _Clawntenna {
3559
3681
  return Number(count);
3560
3682
  }
3561
3683
  async setTopicPermission(topicId, user, permission) {
3562
- if (!this.wallet) throw new Error("Wallet required");
3684
+ this.requireSigner();
3563
3685
  return this.registry.setTopicPermission(topicId, user, permission);
3564
3686
  }
3565
3687
  async getTopicPermission(topicId, user) {
@@ -3568,15 +3690,15 @@ var Clawntenna = class _Clawntenna {
3568
3690
  }
3569
3691
  // ===== MEMBERS =====
3570
3692
  async addMember(appId, address, nickname, roles) {
3571
- if (!this.wallet) throw new Error("Wallet required");
3693
+ this.requireSigner();
3572
3694
  return this.registry.addMember(appId, address, nickname, roles);
3573
3695
  }
3574
3696
  async removeMember(appId, address) {
3575
- if (!this.wallet) throw new Error("Wallet required");
3697
+ this.requireSigner();
3576
3698
  return this.registry.removeMember(appId, address);
3577
3699
  }
3578
3700
  async updateMemberRoles(appId, address, roles) {
3579
- if (!this.wallet) throw new Error("Wallet required");
3701
+ this.requireSigner();
3580
3702
  return this.registry.updateMemberRoles(appId, address, roles);
3581
3703
  }
3582
3704
  async getMember(appId, address) {
@@ -3603,7 +3725,7 @@ var Clawntenna = class _Clawntenna {
3603
3725
  }
3604
3726
  // ===== APPLICATIONS =====
3605
3727
  async createApplication(name, description, frontendUrl, allowPublicTopicCreation) {
3606
- if (!this.wallet) throw new Error("Wallet required");
3728
+ this.requireSigner();
3607
3729
  return this.registry.createApplication(name, description, frontendUrl, allowPublicTopicCreation);
3608
3730
  }
3609
3731
  async getApplicationCount() {
@@ -3611,25 +3733,27 @@ var Clawntenna = class _Clawntenna {
3611
3733
  return Number(count);
3612
3734
  }
3613
3735
  async updateFrontendUrl(appId, frontendUrl) {
3614
- if (!this.wallet) throw new Error("Wallet required");
3736
+ this.requireSigner();
3615
3737
  return this.registry.updateApplicationFrontendUrl(appId, frontendUrl);
3616
3738
  }
3617
3739
  async getApplication(appId) {
3618
- const a = await this.registry.getApplication(appId);
3619
- return {
3620
- id: a.id,
3621
- name: a.name,
3622
- description: a.description,
3623
- frontendUrl: a.frontendUrl,
3624
- owner: a.owner,
3625
- createdAt: a.createdAt,
3626
- memberCount: Number(a.memberCount),
3627
- topicCount: Number(a.topicCount),
3628
- active: a.active,
3629
- allowPublicTopicCreation: a.allowPublicTopicCreation,
3630
- topicCreationFeeToken: a.topicCreationFeeToken,
3631
- topicCreationFeeAmount: a.topicCreationFeeAmount
3632
- };
3740
+ return this._wrapRpcError(async () => {
3741
+ const a = await this.registry.getApplication(appId);
3742
+ return {
3743
+ id: a.id,
3744
+ name: a.name,
3745
+ description: a.description,
3746
+ frontendUrl: a.frontendUrl,
3747
+ owner: a.owner,
3748
+ createdAt: a.createdAt,
3749
+ memberCount: Number(a.memberCount),
3750
+ topicCount: Number(a.topicCount),
3751
+ active: a.active,
3752
+ allowPublicTopicCreation: a.allowPublicTopicCreation,
3753
+ topicCreationFeeToken: a.topicCreationFeeToken,
3754
+ topicCreationFeeAmount: a.topicCreationFeeAmount
3755
+ };
3756
+ }, "getApplication");
3633
3757
  }
3634
3758
  // ===== FEES =====
3635
3759
  async getTopicMessageFee(topicId) {
@@ -3644,7 +3768,7 @@ var Clawntenna = class _Clawntenna {
3644
3768
  * (e.g. '0.15' or 0.15 with USDC → 150000n, '0.01' with native ETH → 10000000000000000n)
3645
3769
  */
3646
3770
  async setTopicCreationFee(appId, feeToken, feeAmount) {
3647
- if (!this.wallet) throw new Error("Wallet required");
3771
+ this.requireSigner();
3648
3772
  const rawAmount = typeof feeAmount === "bigint" ? feeAmount : await this.parseTokenAmount(feeToken, feeAmount);
3649
3773
  return this.registry.setTopicCreationFee(appId, feeToken, rawAmount);
3650
3774
  }
@@ -3656,7 +3780,7 @@ var Clawntenna = class _Clawntenna {
3656
3780
  * (e.g. '0.15' or 0.15 with USDC → 150000n, '0.01' with native ETH → 10000000000000000n)
3657
3781
  */
3658
3782
  async setTopicMessageFee(topicId, feeToken, feeAmount) {
3659
- if (!this.wallet) throw new Error("Wallet required");
3783
+ this.requireSigner();
3660
3784
  const rawAmount = typeof feeAmount === "bigint" ? feeAmount : await this.parseTokenAmount(feeToken, feeAmount);
3661
3785
  return this.registry.setTopicMessageFee(topicId, feeToken, rawAmount);
3662
3786
  }
@@ -3712,14 +3836,14 @@ var Clawntenna = class _Clawntenna {
3712
3836
  * Enable escrow for a topic (topic owner only).
3713
3837
  */
3714
3838
  async enableEscrow(topicId, timeout) {
3715
- if (!this.wallet) throw new Error("Wallet required");
3839
+ this.requireSigner();
3716
3840
  return this.requireEscrow().enableEscrow(topicId, timeout);
3717
3841
  }
3718
3842
  /**
3719
3843
  * Disable escrow for a topic (topic owner only).
3720
3844
  */
3721
3845
  async disableEscrow(topicId) {
3722
- if (!this.wallet) throw new Error("Wallet required");
3846
+ this.requireSigner();
3723
3847
  return this.requireEscrow().disableEscrow(topicId);
3724
3848
  }
3725
3849
  /**
@@ -3780,14 +3904,14 @@ var Clawntenna = class _Clawntenna {
3780
3904
  * Claim a refund for a single deposit.
3781
3905
  */
3782
3906
  async claimRefund(depositId) {
3783
- if (!this.wallet) throw new Error("Wallet required");
3907
+ this.requireSigner();
3784
3908
  return this.requireEscrow().claimRefund(depositId);
3785
3909
  }
3786
3910
  /**
3787
3911
  * Batch claim refunds for multiple deposits.
3788
3912
  */
3789
3913
  async batchClaimRefunds(depositIds) {
3790
- if (!this.wallet) throw new Error("Wallet required");
3914
+ this.requireSigner();
3791
3915
  return this.requireEscrow().batchClaimRefunds(depositIds);
3792
3916
  }
3793
3917
  /**
@@ -3852,10 +3976,11 @@ var Clawntenna = class _Clawntenna {
3852
3976
  * Requires a signer capable of signing messages.
3853
3977
  */
3854
3978
  async deriveECDHFromWallet(appId = 1) {
3855
- if (!this.wallet) throw new Error("Wallet required");
3979
+ this.requireSigner();
3980
+ const signer = this._signer;
3856
3981
  const { privateKey, publicKey } = await deriveKeypairFromSignature(
3857
- this.wallet.address,
3858
- (msg) => this.wallet.signMessage(msg),
3982
+ this.requireAddress(),
3983
+ (msg) => signer.signMessage(msg),
3859
3984
  appId
3860
3985
  );
3861
3986
  this.ecdhPrivateKey = privateKey;
@@ -3874,9 +3999,9 @@ var Clawntenna = class _Clawntenna {
3874
3999
  * Register ECDH public key on-chain.
3875
4000
  */
3876
4001
  async registerPublicKey() {
3877
- if (!this.wallet) throw new Error("Wallet required");
4002
+ this.requireSigner();
3878
4003
  if (!this.ecdhPublicKey) throw new Error("ECDH key not derived yet");
3879
- const hasKey = await this.keyManager.hasPublicKey(this.wallet.address);
4004
+ const hasKey = await this.keyManager.hasPublicKey(this.requireAddress());
3880
4005
  if (hasKey) {
3881
4006
  throw new Error("Public key already registered on-chain");
3882
4007
  }
@@ -3903,13 +4028,13 @@ var Clawntenna = class _Clawntenna {
3903
4028
  * Returns the generated topic key.
3904
4029
  */
3905
4030
  async initializeTopicKey(topicId) {
3906
- if (!this.wallet) throw new Error("Wallet required");
4031
+ this.requireSigner();
3907
4032
  if (!this.ecdhPrivateKey || !this.ecdhPublicKey) {
3908
4033
  throw new Error("ECDH key not derived yet");
3909
4034
  }
3910
4035
  const topicKey = randomBytes(32);
3911
4036
  const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, this.ecdhPublicKey);
3912
- const tx = await this.keyManager.grantKeyAccess(topicId, this.wallet.address, encrypted);
4037
+ const tx = await this.keyManager.grantKeyAccess(topicId, this.requireAddress(), encrypted);
3913
4038
  await tx.wait();
3914
4039
  this.topicKeys.set(topicId, topicKey);
3915
4040
  return topicKey;
@@ -3926,9 +4051,9 @@ var Clawntenna = class _Clawntenna {
3926
4051
  const isNoGrant = err instanceof Error && err.message.includes("No key grant found");
3927
4052
  if (!isNoGrant) throw err;
3928
4053
  const topic = await this.getTopic(topicId);
3929
- if (!this.wallet || topic.owner.toLowerCase() !== this.wallet.address.toLowerCase()) {
4054
+ if (!this._signer || topic.owner.toLowerCase() !== this._address.toLowerCase()) {
3930
4055
  throw new Error(
3931
- `No key grant found for topic ${topicId}. Ask the topic owner to grant you access with: keys grant ${topicId} ${this.wallet?.address ?? "<your-address>"}`
4056
+ `No key grant found for topic ${topicId}. Ask the topic owner to grant you access with: keys grant ${topicId} ${this._address ?? "<your-address>"}`
3932
4057
  );
3933
4058
  }
3934
4059
  return this.initializeTopicKey(topicId);
@@ -3938,7 +4063,7 @@ var Clawntenna = class _Clawntenna {
3938
4063
  * Grant a user access to a private topic's symmetric key.
3939
4064
  */
3940
4065
  async grantKeyAccess(topicId, userAddress, topicKey) {
3941
- if (!this.wallet) throw new Error("Wallet required");
4066
+ this.requireSigner();
3942
4067
  if (!this.ecdhPrivateKey) throw new Error("ECDH key not derived yet");
3943
4068
  const hasKey = await this.keyManager.hasPublicKey(userAddress);
3944
4069
  if (!hasKey) {
@@ -4023,7 +4148,7 @@ var Clawntenna = class _Clawntenna {
4023
4148
  * Batch grant key access to multiple users at once (max 50).
4024
4149
  */
4025
4150
  async batchGrantKeyAccess(topicId, users, topicKey) {
4026
- if (!this.wallet) throw new Error("Wallet required");
4151
+ this.requireSigner();
4027
4152
  if (!this.ecdhPrivateKey) throw new Error("ECDH key not derived yet");
4028
4153
  const encryptedKeys = [];
4029
4154
  for (const user of users) {
@@ -4037,14 +4162,14 @@ var Clawntenna = class _Clawntenna {
4037
4162
  * Revoke a user's key access for a topic.
4038
4163
  */
4039
4164
  async revokeKeyAccess(topicId, address) {
4040
- if (!this.wallet) throw new Error("Wallet required");
4165
+ this.requireSigner();
4041
4166
  return this.keyManager.revokeKeyAccess(topicId, address);
4042
4167
  }
4043
4168
  /**
4044
4169
  * Rotate the key version for a topic. Existing grants become stale.
4045
4170
  */
4046
4171
  async rotateKey(topicId) {
4047
- if (!this.wallet) throw new Error("Wallet required");
4172
+ this.requireSigner();
4048
4173
  return this.keyManager.rotateKey(topicId);
4049
4174
  }
4050
4175
  // ===== SCHEMAS =====
@@ -4052,21 +4177,21 @@ var Clawntenna = class _Clawntenna {
4052
4177
  * Create a schema scoped to an application. Requires app admin role.
4053
4178
  */
4054
4179
  async createAppSchema(appId, name, description, body) {
4055
- if (!this.wallet) throw new Error("Wallet required");
4180
+ this.requireSigner();
4056
4181
  return this.schemaRegistry.createAppSchema(appId, name, description, body);
4057
4182
  }
4058
4183
  /**
4059
4184
  * Publish a new version of an existing schema.
4060
4185
  */
4061
4186
  async publishSchemaVersion(schemaId, body) {
4062
- if (!this.wallet) throw new Error("Wallet required");
4187
+ this.requireSigner();
4063
4188
  return this.schemaRegistry.publishSchemaVersion(schemaId, body);
4064
4189
  }
4065
4190
  /**
4066
4191
  * Deactivate a schema.
4067
4192
  */
4068
4193
  async deactivateSchema(schemaId) {
4069
- if (!this.wallet) throw new Error("Wallet required");
4194
+ this.requireSigner();
4070
4195
  return this.schemaRegistry.deactivateSchema(schemaId);
4071
4196
  }
4072
4197
  /**
@@ -4118,14 +4243,14 @@ var Clawntenna = class _Clawntenna {
4118
4243
  * Bind a schema version to a topic. Requires topic admin.
4119
4244
  */
4120
4245
  async setTopicSchema(topicId, schemaId, version) {
4121
- if (!this.wallet) throw new Error("Wallet required");
4246
+ this.requireSigner();
4122
4247
  return this.schemaRegistry.setTopicSchema(topicId, schemaId, version);
4123
4248
  }
4124
4249
  /**
4125
4250
  * Remove schema binding from a topic.
4126
4251
  */
4127
4252
  async clearTopicSchema(topicId) {
4128
- if (!this.wallet) throw new Error("Wallet required");
4253
+ this.requireSigner();
4129
4254
  return this.schemaRegistry.clearTopicSchema(topicId);
4130
4255
  }
4131
4256
  // ===== AGENT IDENTITY (V5) =====
@@ -4134,14 +4259,14 @@ var Clawntenna = class _Clawntenna {
4134
4259
  * Verifies ownership via ownerOf on the identity registry.
4135
4260
  */
4136
4261
  async registerAgentIdentity(appId, tokenId) {
4137
- if (!this.wallet) throw new Error("Wallet required");
4262
+ this.requireSigner();
4138
4263
  return this.registry.registerAgentIdentity(appId, tokenId);
4139
4264
  }
4140
4265
  /**
4141
4266
  * Clear your agent identity registration for an application (V5).
4142
4267
  */
4143
4268
  async clearAgentIdentity(appId) {
4144
- if (!this.wallet) throw new Error("Wallet required");
4269
+ this.requireSigner();
4145
4270
  return this.registry.clearAgentIdentity(appId);
4146
4271
  }
4147
4272
  /**
@@ -4170,7 +4295,7 @@ var Clawntenna = class _Clawntenna {
4170
4295
  * Optionally provide a URI for the agent's metadata.
4171
4296
  */
4172
4297
  async registerAgent(agentURI) {
4173
- if (!this.wallet) throw new Error("Wallet required");
4298
+ this.requireSigner();
4174
4299
  const registry = this.requireIdentityRegistry();
4175
4300
  const tx = agentURI ? await registry["register(string)"](agentURI) : await registry["register()"]();
4176
4301
  const receipt = await tx.wait();
@@ -4193,7 +4318,7 @@ var Clawntenna = class _Clawntenna {
4193
4318
  * Register as an agent with metadata entries.
4194
4319
  */
4195
4320
  async registerAgentWithMetadata(agentURI, metadata) {
4196
- if (!this.wallet) throw new Error("Wallet required");
4321
+ this.requireSigner();
4197
4322
  const registry = this.requireIdentityRegistry();
4198
4323
  const tx = await registry["register(string,(string,bytes)[])"](agentURI, metadata);
4199
4324
  const receipt = await tx.wait();
@@ -4218,7 +4343,7 @@ var Clawntenna = class _Clawntenna {
4218
4343
  */
4219
4344
  async isRegisteredAgent(address) {
4220
4345
  const registry = this.requireIdentityRegistry();
4221
- const addr = address ?? this.wallet?.address;
4346
+ const addr = address ?? this._address;
4222
4347
  if (!addr) throw new Error("Address required");
4223
4348
  const balance = await registry.balanceOf(addr);
4224
4349
  return balance > 0n;
@@ -4239,7 +4364,7 @@ var Clawntenna = class _Clawntenna {
4239
4364
  * Set metadata for an agent.
4240
4365
  */
4241
4366
  async setAgentMetadata(agentId, key, value) {
4242
- if (!this.wallet) throw new Error("Wallet required");
4367
+ this.requireSigner();
4243
4368
  const registry = this.requireIdentityRegistry();
4244
4369
  return registry.setMetadata(agentId, key, value);
4245
4370
  }
@@ -4255,7 +4380,7 @@ var Clawntenna = class _Clawntenna {
4255
4380
  * Update the URI for an agent registration.
4256
4381
  */
4257
4382
  async setAgentURI(agentId, newURI) {
4258
- if (!this.wallet) throw new Error("Wallet required");
4383
+ this.requireSigner();
4259
4384
  const registry = this.requireIdentityRegistry();
4260
4385
  return registry.setAgentURI(agentId, newURI);
4261
4386
  }
@@ -5594,7 +5719,7 @@ function decodeContractError(err) {
5594
5719
  }
5595
5720
 
5596
5721
  // src/cli/index.ts
5597
- var VERSION = "0.9.0";
5722
+ var VERSION = "0.10.1";
5598
5723
  var HELP = `
5599
5724
  clawntenna v${VERSION}
5600
5725
  On-chain encrypted messaging for AI agents