clawntenna 0.9.0 → 0.10.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/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,51 @@ 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
+ return null;
3373
+ }
3374
+
3358
3375
  // src/client.ts
3359
3376
  var Clawntenna = class _Clawntenna {
3360
3377
  provider;
3361
- wallet;
3362
- registry;
3363
- keyManager;
3364
- schemaRegistry;
3365
- identityRegistry;
3366
- escrow;
3367
3378
  chainName;
3379
+ _signer;
3380
+ _address;
3381
+ _registry;
3382
+ _keyManager;
3383
+ _schemaRegistry;
3384
+ _identityRegistry;
3385
+ _escrow;
3386
+ /** @deprecated Use `signer` instead. */
3387
+ get wallet() {
3388
+ return this._signer;
3389
+ }
3390
+ get signer() {
3391
+ return this._signer;
3392
+ }
3393
+ get registry() {
3394
+ return this._registry;
3395
+ }
3396
+ get keyManager() {
3397
+ return this._keyManager;
3398
+ }
3399
+ get schemaRegistry() {
3400
+ return this._schemaRegistry;
3401
+ }
3402
+ get identityRegistry() {
3403
+ return this._identityRegistry;
3404
+ }
3405
+ get escrow() {
3406
+ return this._escrow;
3407
+ }
3368
3408
  // In-memory ECDH state
3369
3409
  ecdhPrivateKey = null;
3370
3410
  ecdhPublicKey = null;
@@ -3385,23 +3425,56 @@ var Clawntenna = class _Clawntenna {
3385
3425
  const escrowAddr = options.escrowAddress ?? chain.escrow;
3386
3426
  const signer = options.privateKey ? new ethers.Wallet(options.privateKey, this.provider) : null;
3387
3427
  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;
3428
+ this._signer = signer;
3429
+ this._address = signer?.address ?? null;
3430
+ this._registry = new ethers.Contract(registryAddr, REGISTRY_ABI, runner);
3431
+ this._keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, runner);
3432
+ this._schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, runner);
3433
+ this._identityRegistry = chain.identityRegistry ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, runner) : null;
3434
+ this._escrow = escrowAddr ? new ethers.Contract(escrowAddr, ESCROW_ABI, runner) : null;
3435
+ }
3436
+ /**
3437
+ * Connect an external signer (e.g. from BrowserProvider).
3438
+ * Reconnects all contract instances to the new signer.
3439
+ */
3440
+ async connectSigner(signer) {
3441
+ this._address = await signer.getAddress();
3442
+ this._signer = signer;
3443
+ this._registry = this._registry.connect(signer);
3444
+ this._keyManager = this._keyManager.connect(signer);
3445
+ this._schemaRegistry = this._schemaRegistry.connect(signer);
3446
+ if (this._identityRegistry) {
3447
+ this._identityRegistry = this._identityRegistry.connect(signer);
3448
+ }
3449
+ if (this._escrow) {
3450
+ this._escrow = this._escrow.connect(signer);
3451
+ }
3452
+ }
3453
+ requireSigner() {
3454
+ if (!this._signer) {
3455
+ throw new Error("Signer required. Pass privateKey in constructor or call connectSigner().");
3456
+ }
3457
+ return this._signer;
3458
+ }
3459
+ requireAddress() {
3460
+ if (!this._address) {
3461
+ throw new Error("Signer required. Pass privateKey in constructor or call connectSigner().");
3462
+ }
3463
+ return this._address;
3464
+ }
3465
+ async _wrapRpcError(fn, method) {
3466
+ try {
3467
+ return await fn();
3468
+ } catch (err) {
3469
+ if (err instanceof Error) {
3470
+ const hint = classifyRpcError(err, { method, chainName: this.chainName });
3471
+ if (hint) throw new Error(hint, { cause: err });
3472
+ }
3473
+ throw err;
3400
3474
  }
3401
- this.escrow = escrowAddr ? new ethers.Contract(escrowAddr, ESCROW_ABI, runner) : null;
3402
3475
  }
3403
3476
  get address() {
3404
- return this.wallet?.address ?? null;
3477
+ return this._address;
3405
3478
  }
3406
3479
  // ===== MESSAGING =====
3407
3480
  /**
@@ -3409,7 +3482,7 @@ var Clawntenna = class _Clawntenna {
3409
3482
  * Automatically determines encryption key based on topic access level.
3410
3483
  */
3411
3484
  async sendMessage(topicId, text, options) {
3412
- if (!this.wallet) throw new Error("Wallet required to send messages");
3485
+ this.requireSigner();
3413
3486
  if (options?.replyTo && this.escrow && !options?.skipRefundCheck) {
3414
3487
  const refunded = await this.isMessageRefunded(options.replyTo);
3415
3488
  if (refunded) {
@@ -3447,13 +3520,17 @@ var Clawntenna = class _Clawntenna {
3447
3520
  const filter = this.registry.filters.MessageSent(topicId);
3448
3521
  const CHUNK_SIZE = 2e3;
3449
3522
  const currentBlock = await this.provider.getBlockNumber();
3450
- const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : 1e5;
3523
+ const chain = CHAINS[this.chainName];
3524
+ const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : chain.defaultLookback;
3451
3525
  const startBlock = currentBlock - maxRange;
3452
3526
  const allEvents = [];
3453
3527
  let toBlock = currentBlock;
3454
3528
  while (toBlock > startBlock && allEvents.length < limit) {
3455
3529
  const chunkFrom = Math.max(toBlock - CHUNK_SIZE + 1, startBlock);
3456
- const events = await this.registry.queryFilter(filter, chunkFrom, toBlock);
3530
+ const events = await this._wrapRpcError(
3531
+ () => this.registry.queryFilter(filter, chunkFrom, toBlock),
3532
+ "readMessages"
3533
+ );
3457
3534
  allEvents.unshift(...events);
3458
3535
  toBlock = chunkFrom - 1;
3459
3536
  }
@@ -3463,12 +3540,12 @@ var Clawntenna = class _Clawntenna {
3463
3540
  const payloadStr = ethers.toUtf8String(log.args.payload);
3464
3541
  const parsed = decryptMessage(payloadStr, key);
3465
3542
  messages.push({
3466
- topicId: BigInt(topicId),
3543
+ topicId,
3467
3544
  sender: log.args.sender,
3468
3545
  text: parsed?.text ?? "[decryption failed]",
3469
3546
  replyTo: parsed?.replyTo ?? null,
3470
3547
  mentions: parsed?.mentions ?? null,
3471
- timestamp: log.args.timestamp,
3548
+ timestamp: Number(log.args.timestamp),
3472
3549
  txHash: log.transactionHash,
3473
3550
  blockNumber: log.blockNumber
3474
3551
  });
@@ -3489,12 +3566,12 @@ var Clawntenna = class _Clawntenna {
3489
3566
  const payloadStr = ethers.toUtf8String(payload);
3490
3567
  const parsed = decryptMessage(payloadStr, key);
3491
3568
  callback({
3492
- topicId: tId,
3569
+ topicId: Number(tId),
3493
3570
  sender,
3494
3571
  text: parsed?.text ?? "[decryption failed]",
3495
3572
  replyTo: parsed?.replyTo ?? null,
3496
3573
  mentions: parsed?.mentions ?? null,
3497
- timestamp,
3574
+ timestamp: Number(timestamp),
3498
3575
  txHash: event.transactionHash,
3499
3576
  blockNumber: event.blockNumber
3500
3577
  });
@@ -3506,7 +3583,7 @@ var Clawntenna = class _Clawntenna {
3506
3583
  }
3507
3584
  // ===== NICKNAMES =====
3508
3585
  async setNickname(appId, nickname) {
3509
- if (!this.wallet) throw new Error("Wallet required");
3586
+ this.requireSigner();
3510
3587
  return this.registry.setNickname(appId, nickname);
3511
3588
  }
3512
3589
  async getNickname(appId, address) {
@@ -3520,11 +3597,11 @@ var Clawntenna = class _Clawntenna {
3520
3597
  return { canChange, timeRemaining };
3521
3598
  }
3522
3599
  async clearNickname(appId) {
3523
- if (!this.wallet) throw new Error("Wallet required");
3600
+ this.requireSigner();
3524
3601
  return this.registry.clearNickname(appId);
3525
3602
  }
3526
3603
  async setNicknameCooldown(appId, cooldownSeconds) {
3527
- if (!this.wallet) throw new Error("Wallet required");
3604
+ this.requireSigner();
3528
3605
  return this.registry.setNicknameCooldown(appId, cooldownSeconds);
3529
3606
  }
3530
3607
  async getNicknameCooldown(appId) {
@@ -3532,24 +3609,26 @@ var Clawntenna = class _Clawntenna {
3532
3609
  }
3533
3610
  // ===== TOPICS =====
3534
3611
  async createTopic(appId, name, description, accessLevel) {
3535
- if (!this.wallet) throw new Error("Wallet required");
3612
+ this.requireSigner();
3536
3613
  return this.registry.createTopic(appId, name, description, accessLevel);
3537
3614
  }
3538
3615
  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
- };
3616
+ return this._wrapRpcError(async () => {
3617
+ const t = await this.registry.getTopic(topicId);
3618
+ return {
3619
+ id: t.id,
3620
+ applicationId: t.applicationId,
3621
+ name: t.name,
3622
+ description: t.description,
3623
+ owner: t.owner,
3624
+ creator: t.creator,
3625
+ createdAt: t.createdAt,
3626
+ lastMessageAt: t.lastMessageAt,
3627
+ messageCount: t.messageCount,
3628
+ accessLevel: Number(t.accessLevel),
3629
+ active: t.active
3630
+ };
3631
+ }, "getTopic");
3553
3632
  }
3554
3633
  async getApplicationTopics(appId) {
3555
3634
  return this.registry.getApplicationTopics(appId);
@@ -3559,7 +3638,7 @@ var Clawntenna = class _Clawntenna {
3559
3638
  return Number(count);
3560
3639
  }
3561
3640
  async setTopicPermission(topicId, user, permission) {
3562
- if (!this.wallet) throw new Error("Wallet required");
3641
+ this.requireSigner();
3563
3642
  return this.registry.setTopicPermission(topicId, user, permission);
3564
3643
  }
3565
3644
  async getTopicPermission(topicId, user) {
@@ -3568,15 +3647,15 @@ var Clawntenna = class _Clawntenna {
3568
3647
  }
3569
3648
  // ===== MEMBERS =====
3570
3649
  async addMember(appId, address, nickname, roles) {
3571
- if (!this.wallet) throw new Error("Wallet required");
3650
+ this.requireSigner();
3572
3651
  return this.registry.addMember(appId, address, nickname, roles);
3573
3652
  }
3574
3653
  async removeMember(appId, address) {
3575
- if (!this.wallet) throw new Error("Wallet required");
3654
+ this.requireSigner();
3576
3655
  return this.registry.removeMember(appId, address);
3577
3656
  }
3578
3657
  async updateMemberRoles(appId, address, roles) {
3579
- if (!this.wallet) throw new Error("Wallet required");
3658
+ this.requireSigner();
3580
3659
  return this.registry.updateMemberRoles(appId, address, roles);
3581
3660
  }
3582
3661
  async getMember(appId, address) {
@@ -3603,7 +3682,7 @@ var Clawntenna = class _Clawntenna {
3603
3682
  }
3604
3683
  // ===== APPLICATIONS =====
3605
3684
  async createApplication(name, description, frontendUrl, allowPublicTopicCreation) {
3606
- if (!this.wallet) throw new Error("Wallet required");
3685
+ this.requireSigner();
3607
3686
  return this.registry.createApplication(name, description, frontendUrl, allowPublicTopicCreation);
3608
3687
  }
3609
3688
  async getApplicationCount() {
@@ -3611,25 +3690,27 @@ var Clawntenna = class _Clawntenna {
3611
3690
  return Number(count);
3612
3691
  }
3613
3692
  async updateFrontendUrl(appId, frontendUrl) {
3614
- if (!this.wallet) throw new Error("Wallet required");
3693
+ this.requireSigner();
3615
3694
  return this.registry.updateApplicationFrontendUrl(appId, frontendUrl);
3616
3695
  }
3617
3696
  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
- };
3697
+ return this._wrapRpcError(async () => {
3698
+ const a = await this.registry.getApplication(appId);
3699
+ return {
3700
+ id: a.id,
3701
+ name: a.name,
3702
+ description: a.description,
3703
+ frontendUrl: a.frontendUrl,
3704
+ owner: a.owner,
3705
+ createdAt: a.createdAt,
3706
+ memberCount: Number(a.memberCount),
3707
+ topicCount: Number(a.topicCount),
3708
+ active: a.active,
3709
+ allowPublicTopicCreation: a.allowPublicTopicCreation,
3710
+ topicCreationFeeToken: a.topicCreationFeeToken,
3711
+ topicCreationFeeAmount: a.topicCreationFeeAmount
3712
+ };
3713
+ }, "getApplication");
3633
3714
  }
3634
3715
  // ===== FEES =====
3635
3716
  async getTopicMessageFee(topicId) {
@@ -3644,7 +3725,7 @@ var Clawntenna = class _Clawntenna {
3644
3725
  * (e.g. '0.15' or 0.15 with USDC → 150000n, '0.01' with native ETH → 10000000000000000n)
3645
3726
  */
3646
3727
  async setTopicCreationFee(appId, feeToken, feeAmount) {
3647
- if (!this.wallet) throw new Error("Wallet required");
3728
+ this.requireSigner();
3648
3729
  const rawAmount = typeof feeAmount === "bigint" ? feeAmount : await this.parseTokenAmount(feeToken, feeAmount);
3649
3730
  return this.registry.setTopicCreationFee(appId, feeToken, rawAmount);
3650
3731
  }
@@ -3656,7 +3737,7 @@ var Clawntenna = class _Clawntenna {
3656
3737
  * (e.g. '0.15' or 0.15 with USDC → 150000n, '0.01' with native ETH → 10000000000000000n)
3657
3738
  */
3658
3739
  async setTopicMessageFee(topicId, feeToken, feeAmount) {
3659
- if (!this.wallet) throw new Error("Wallet required");
3740
+ this.requireSigner();
3660
3741
  const rawAmount = typeof feeAmount === "bigint" ? feeAmount : await this.parseTokenAmount(feeToken, feeAmount);
3661
3742
  return this.registry.setTopicMessageFee(topicId, feeToken, rawAmount);
3662
3743
  }
@@ -3712,14 +3793,14 @@ var Clawntenna = class _Clawntenna {
3712
3793
  * Enable escrow for a topic (topic owner only).
3713
3794
  */
3714
3795
  async enableEscrow(topicId, timeout) {
3715
- if (!this.wallet) throw new Error("Wallet required");
3796
+ this.requireSigner();
3716
3797
  return this.requireEscrow().enableEscrow(topicId, timeout);
3717
3798
  }
3718
3799
  /**
3719
3800
  * Disable escrow for a topic (topic owner only).
3720
3801
  */
3721
3802
  async disableEscrow(topicId) {
3722
- if (!this.wallet) throw new Error("Wallet required");
3803
+ this.requireSigner();
3723
3804
  return this.requireEscrow().disableEscrow(topicId);
3724
3805
  }
3725
3806
  /**
@@ -3780,14 +3861,14 @@ var Clawntenna = class _Clawntenna {
3780
3861
  * Claim a refund for a single deposit.
3781
3862
  */
3782
3863
  async claimRefund(depositId) {
3783
- if (!this.wallet) throw new Error("Wallet required");
3864
+ this.requireSigner();
3784
3865
  return this.requireEscrow().claimRefund(depositId);
3785
3866
  }
3786
3867
  /**
3787
3868
  * Batch claim refunds for multiple deposits.
3788
3869
  */
3789
3870
  async batchClaimRefunds(depositIds) {
3790
- if (!this.wallet) throw new Error("Wallet required");
3871
+ this.requireSigner();
3791
3872
  return this.requireEscrow().batchClaimRefunds(depositIds);
3792
3873
  }
3793
3874
  /**
@@ -3852,10 +3933,11 @@ var Clawntenna = class _Clawntenna {
3852
3933
  * Requires a signer capable of signing messages.
3853
3934
  */
3854
3935
  async deriveECDHFromWallet(appId = 1) {
3855
- if (!this.wallet) throw new Error("Wallet required");
3936
+ this.requireSigner();
3937
+ const signer = this._signer;
3856
3938
  const { privateKey, publicKey } = await deriveKeypairFromSignature(
3857
- this.wallet.address,
3858
- (msg) => this.wallet.signMessage(msg),
3939
+ this.requireAddress(),
3940
+ (msg) => signer.signMessage(msg),
3859
3941
  appId
3860
3942
  );
3861
3943
  this.ecdhPrivateKey = privateKey;
@@ -3874,9 +3956,9 @@ var Clawntenna = class _Clawntenna {
3874
3956
  * Register ECDH public key on-chain.
3875
3957
  */
3876
3958
  async registerPublicKey() {
3877
- if (!this.wallet) throw new Error("Wallet required");
3959
+ this.requireSigner();
3878
3960
  if (!this.ecdhPublicKey) throw new Error("ECDH key not derived yet");
3879
- const hasKey = await this.keyManager.hasPublicKey(this.wallet.address);
3961
+ const hasKey = await this.keyManager.hasPublicKey(this.requireAddress());
3880
3962
  if (hasKey) {
3881
3963
  throw new Error("Public key already registered on-chain");
3882
3964
  }
@@ -3903,13 +3985,13 @@ var Clawntenna = class _Clawntenna {
3903
3985
  * Returns the generated topic key.
3904
3986
  */
3905
3987
  async initializeTopicKey(topicId) {
3906
- if (!this.wallet) throw new Error("Wallet required");
3988
+ this.requireSigner();
3907
3989
  if (!this.ecdhPrivateKey || !this.ecdhPublicKey) {
3908
3990
  throw new Error("ECDH key not derived yet");
3909
3991
  }
3910
3992
  const topicKey = randomBytes(32);
3911
3993
  const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, this.ecdhPublicKey);
3912
- const tx = await this.keyManager.grantKeyAccess(topicId, this.wallet.address, encrypted);
3994
+ const tx = await this.keyManager.grantKeyAccess(topicId, this.requireAddress(), encrypted);
3913
3995
  await tx.wait();
3914
3996
  this.topicKeys.set(topicId, topicKey);
3915
3997
  return topicKey;
@@ -3926,9 +4008,9 @@ var Clawntenna = class _Clawntenna {
3926
4008
  const isNoGrant = err instanceof Error && err.message.includes("No key grant found");
3927
4009
  if (!isNoGrant) throw err;
3928
4010
  const topic = await this.getTopic(topicId);
3929
- if (!this.wallet || topic.owner.toLowerCase() !== this.wallet.address.toLowerCase()) {
4011
+ if (!this._signer || topic.owner.toLowerCase() !== this._address.toLowerCase()) {
3930
4012
  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>"}`
4013
+ `No key grant found for topic ${topicId}. Ask the topic owner to grant you access with: keys grant ${topicId} ${this._address ?? "<your-address>"}`
3932
4014
  );
3933
4015
  }
3934
4016
  return this.initializeTopicKey(topicId);
@@ -3938,7 +4020,7 @@ var Clawntenna = class _Clawntenna {
3938
4020
  * Grant a user access to a private topic's symmetric key.
3939
4021
  */
3940
4022
  async grantKeyAccess(topicId, userAddress, topicKey) {
3941
- if (!this.wallet) throw new Error("Wallet required");
4023
+ this.requireSigner();
3942
4024
  if (!this.ecdhPrivateKey) throw new Error("ECDH key not derived yet");
3943
4025
  const hasKey = await this.keyManager.hasPublicKey(userAddress);
3944
4026
  if (!hasKey) {
@@ -4023,7 +4105,7 @@ var Clawntenna = class _Clawntenna {
4023
4105
  * Batch grant key access to multiple users at once (max 50).
4024
4106
  */
4025
4107
  async batchGrantKeyAccess(topicId, users, topicKey) {
4026
- if (!this.wallet) throw new Error("Wallet required");
4108
+ this.requireSigner();
4027
4109
  if (!this.ecdhPrivateKey) throw new Error("ECDH key not derived yet");
4028
4110
  const encryptedKeys = [];
4029
4111
  for (const user of users) {
@@ -4037,14 +4119,14 @@ var Clawntenna = class _Clawntenna {
4037
4119
  * Revoke a user's key access for a topic.
4038
4120
  */
4039
4121
  async revokeKeyAccess(topicId, address) {
4040
- if (!this.wallet) throw new Error("Wallet required");
4122
+ this.requireSigner();
4041
4123
  return this.keyManager.revokeKeyAccess(topicId, address);
4042
4124
  }
4043
4125
  /**
4044
4126
  * Rotate the key version for a topic. Existing grants become stale.
4045
4127
  */
4046
4128
  async rotateKey(topicId) {
4047
- if (!this.wallet) throw new Error("Wallet required");
4129
+ this.requireSigner();
4048
4130
  return this.keyManager.rotateKey(topicId);
4049
4131
  }
4050
4132
  // ===== SCHEMAS =====
@@ -4052,21 +4134,21 @@ var Clawntenna = class _Clawntenna {
4052
4134
  * Create a schema scoped to an application. Requires app admin role.
4053
4135
  */
4054
4136
  async createAppSchema(appId, name, description, body) {
4055
- if (!this.wallet) throw new Error("Wallet required");
4137
+ this.requireSigner();
4056
4138
  return this.schemaRegistry.createAppSchema(appId, name, description, body);
4057
4139
  }
4058
4140
  /**
4059
4141
  * Publish a new version of an existing schema.
4060
4142
  */
4061
4143
  async publishSchemaVersion(schemaId, body) {
4062
- if (!this.wallet) throw new Error("Wallet required");
4144
+ this.requireSigner();
4063
4145
  return this.schemaRegistry.publishSchemaVersion(schemaId, body);
4064
4146
  }
4065
4147
  /**
4066
4148
  * Deactivate a schema.
4067
4149
  */
4068
4150
  async deactivateSchema(schemaId) {
4069
- if (!this.wallet) throw new Error("Wallet required");
4151
+ this.requireSigner();
4070
4152
  return this.schemaRegistry.deactivateSchema(schemaId);
4071
4153
  }
4072
4154
  /**
@@ -4118,14 +4200,14 @@ var Clawntenna = class _Clawntenna {
4118
4200
  * Bind a schema version to a topic. Requires topic admin.
4119
4201
  */
4120
4202
  async setTopicSchema(topicId, schemaId, version) {
4121
- if (!this.wallet) throw new Error("Wallet required");
4203
+ this.requireSigner();
4122
4204
  return this.schemaRegistry.setTopicSchema(topicId, schemaId, version);
4123
4205
  }
4124
4206
  /**
4125
4207
  * Remove schema binding from a topic.
4126
4208
  */
4127
4209
  async clearTopicSchema(topicId) {
4128
- if (!this.wallet) throw new Error("Wallet required");
4210
+ this.requireSigner();
4129
4211
  return this.schemaRegistry.clearTopicSchema(topicId);
4130
4212
  }
4131
4213
  // ===== AGENT IDENTITY (V5) =====
@@ -4134,14 +4216,14 @@ var Clawntenna = class _Clawntenna {
4134
4216
  * Verifies ownership via ownerOf on the identity registry.
4135
4217
  */
4136
4218
  async registerAgentIdentity(appId, tokenId) {
4137
- if (!this.wallet) throw new Error("Wallet required");
4219
+ this.requireSigner();
4138
4220
  return this.registry.registerAgentIdentity(appId, tokenId);
4139
4221
  }
4140
4222
  /**
4141
4223
  * Clear your agent identity registration for an application (V5).
4142
4224
  */
4143
4225
  async clearAgentIdentity(appId) {
4144
- if (!this.wallet) throw new Error("Wallet required");
4226
+ this.requireSigner();
4145
4227
  return this.registry.clearAgentIdentity(appId);
4146
4228
  }
4147
4229
  /**
@@ -4170,7 +4252,7 @@ var Clawntenna = class _Clawntenna {
4170
4252
  * Optionally provide a URI for the agent's metadata.
4171
4253
  */
4172
4254
  async registerAgent(agentURI) {
4173
- if (!this.wallet) throw new Error("Wallet required");
4255
+ this.requireSigner();
4174
4256
  const registry = this.requireIdentityRegistry();
4175
4257
  const tx = agentURI ? await registry["register(string)"](agentURI) : await registry["register()"]();
4176
4258
  const receipt = await tx.wait();
@@ -4193,7 +4275,7 @@ var Clawntenna = class _Clawntenna {
4193
4275
  * Register as an agent with metadata entries.
4194
4276
  */
4195
4277
  async registerAgentWithMetadata(agentURI, metadata) {
4196
- if (!this.wallet) throw new Error("Wallet required");
4278
+ this.requireSigner();
4197
4279
  const registry = this.requireIdentityRegistry();
4198
4280
  const tx = await registry["register(string,(string,bytes)[])"](agentURI, metadata);
4199
4281
  const receipt = await tx.wait();
@@ -4218,7 +4300,7 @@ var Clawntenna = class _Clawntenna {
4218
4300
  */
4219
4301
  async isRegisteredAgent(address) {
4220
4302
  const registry = this.requireIdentityRegistry();
4221
- const addr = address ?? this.wallet?.address;
4303
+ const addr = address ?? this._address;
4222
4304
  if (!addr) throw new Error("Address required");
4223
4305
  const balance = await registry.balanceOf(addr);
4224
4306
  return balance > 0n;
@@ -4239,7 +4321,7 @@ var Clawntenna = class _Clawntenna {
4239
4321
  * Set metadata for an agent.
4240
4322
  */
4241
4323
  async setAgentMetadata(agentId, key, value) {
4242
- if (!this.wallet) throw new Error("Wallet required");
4324
+ this.requireSigner();
4243
4325
  const registry = this.requireIdentityRegistry();
4244
4326
  return registry.setMetadata(agentId, key, value);
4245
4327
  }
@@ -4255,7 +4337,7 @@ var Clawntenna = class _Clawntenna {
4255
4337
  * Update the URI for an agent registration.
4256
4338
  */
4257
4339
  async setAgentURI(agentId, newURI) {
4258
- if (!this.wallet) throw new Error("Wallet required");
4340
+ this.requireSigner();
4259
4341
  const registry = this.requireIdentityRegistry();
4260
4342
  return registry.setAgentURI(agentId, newURI);
4261
4343
  }
@@ -5594,7 +5676,7 @@ function decodeContractError(err) {
5594
5676
  }
5595
5677
 
5596
5678
  // src/cli/index.ts
5597
- var VERSION = "0.9.0";
5679
+ var VERSION = "0.10.0";
5598
5680
  var HELP = `
5599
5681
  clawntenna v${VERSION}
5600
5682
  On-chain encrypted messaging for AI agents