clawntenna 0.11.2 → 0.11.4

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/README.md CHANGED
@@ -315,7 +315,7 @@ await client.sendMessage(topicId, 'reply', {
315
315
  ### Schemas
316
316
 
317
317
  ```ts
318
- // Create app-scoped schema (app admin)
318
+ // Create app-scoped schema (app admin) — CLI --json returns { txHash, blockNumber, appId, schemaId }
319
319
  await client.createAppSchema(appId, 'chat-v1', 'Chat format', JSON.stringify({
320
320
  "$schema": "clawntenna-message-v1",
321
321
  "type": "object",
package/dist/cli/index.js CHANGED
@@ -3571,7 +3571,53 @@ var Clawntenna = class _Clawntenna {
3571
3571
  replyAuthor,
3572
3572
  mentions: options?.mentions
3573
3573
  });
3574
- return this.registry.sendMessage(topicId, ethers.toUtf8Bytes(encrypted));
3574
+ const txOverrides = {};
3575
+ try {
3576
+ const fee = await this.getTopicMessageFee(topicId);
3577
+ if (fee.amount > BigInt(0)) {
3578
+ const exempt = await this._isFeeExempt(topicId);
3579
+ if (!exempt) {
3580
+ if (fee.token === ethers.ZeroAddress) {
3581
+ txOverrides.value = fee.amount;
3582
+ } else {
3583
+ const token = new ethers.Contract(fee.token, [
3584
+ "function allowance(address,address) view returns (uint256)",
3585
+ "function approve(address,uint256) returns (bool)"
3586
+ ], this._signer);
3587
+ const registryAddr = await this._registry.getAddress();
3588
+ const allowance = await token.allowance(this._address, registryAddr);
3589
+ if (allowance < fee.amount) {
3590
+ const approveTx = await token.approve(registryAddr, fee.amount);
3591
+ await approveTx.wait();
3592
+ }
3593
+ }
3594
+ }
3595
+ }
3596
+ } catch {
3597
+ }
3598
+ return this.registry.sendMessage(topicId, ethers.toUtf8Bytes(encrypted), txOverrides);
3599
+ }
3600
+ /**
3601
+ * Check if the current signer is exempt from message fees on a topic.
3602
+ * Mirrors contract logic: topic owner, app owner, ROLE_ADMIN, PERMISSION_ADMIN.
3603
+ */
3604
+ async _isFeeExempt(topicId) {
3605
+ const addr = this._address.toLowerCase();
3606
+ const topic = await this.getTopic(topicId);
3607
+ if (topic.owner.toLowerCase() === addr) return true;
3608
+ const app = await this.getApplication(Number(topic.applicationId));
3609
+ if (app.owner.toLowerCase() === addr) return true;
3610
+ try {
3611
+ const member = await this.getMember(Number(topic.applicationId), this._address);
3612
+ if ((member.roles & 8 /* ADMIN */) !== 0) return true;
3613
+ } catch {
3614
+ }
3615
+ try {
3616
+ const perm = await this.getTopicPermission(topicId, this._address);
3617
+ if (perm === 4 /* ADMIN */) return true;
3618
+ } catch {
3619
+ }
3620
+ return false;
3575
3621
  }
3576
3622
  /**
3577
3623
  * Read and decrypt recent messages from a topic.
@@ -3672,7 +3718,28 @@ var Clawntenna = class _Clawntenna {
3672
3718
  // ===== TOPICS =====
3673
3719
  async createTopic(appId, name, description, accessLevel) {
3674
3720
  this.requireSigner();
3675
- return this.registry.createTopic(appId, name, description, accessLevel);
3721
+ const txOverrides = {};
3722
+ try {
3723
+ const app = await this.getApplication(appId);
3724
+ const feeAmount = app.topicCreationFeeAmount ?? BigInt(0);
3725
+ const feeToken = app.topicCreationFeeToken ?? ethers.ZeroAddress;
3726
+ if (feeAmount > BigInt(0) && feeToken === ethers.ZeroAddress) {
3727
+ txOverrides.value = feeAmount;
3728
+ } else if (feeAmount > BigInt(0)) {
3729
+ const token = new ethers.Contract(feeToken, [
3730
+ "function allowance(address,address) view returns (uint256)",
3731
+ "function approve(address,uint256) returns (bool)"
3732
+ ], this._signer);
3733
+ const registryAddr = await this._registry.getAddress();
3734
+ const allowance = await token.allowance(this._address, registryAddr);
3735
+ if (allowance < feeAmount) {
3736
+ const approveTx = await token.approve(registryAddr, feeAmount);
3737
+ await approveTx.wait();
3738
+ }
3739
+ }
3740
+ } catch {
3741
+ }
3742
+ return this.registry.createTopic(appId, name, description, accessLevel, txOverrides);
3676
3743
  }
3677
3744
  async getTopic(topicId) {
3678
3745
  return this._wrapRpcError(async () => {
@@ -5487,9 +5554,23 @@ async function schemaCreate(appId, name, description, body, flags) {
5487
5554
  if (!json) console.log(`Creating schema "${name}" in app ${appId}...`);
5488
5555
  const tx = await client.createAppSchema(appId, name, description, body);
5489
5556
  const receipt = await tx.wait();
5557
+ let schemaId = null;
5558
+ if (receipt) {
5559
+ for (const log of receipt.logs) {
5560
+ try {
5561
+ const parsed = client.schemaRegistry.interface.parseLog(log);
5562
+ if (parsed?.name === "AppSchemaCreated" || parsed?.name === "SchemaCreated") {
5563
+ schemaId = Number(parsed.args.schemaId);
5564
+ break;
5565
+ }
5566
+ } catch {
5567
+ }
5568
+ }
5569
+ }
5490
5570
  if (json) {
5491
- output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, appId }, true);
5571
+ output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, appId, schemaId }, true);
5492
5572
  } else {
5573
+ console.log(`Schema created${schemaId ? ` (ID: ${schemaId})` : ""}`);
5493
5574
  console.log(`TX: ${tx.hash}`);
5494
5575
  console.log(`Confirmed in block ${receipt?.blockNumber}`);
5495
5576
  }
@@ -6127,7 +6208,7 @@ function decodeContractError(err) {
6127
6208
  }
6128
6209
 
6129
6210
  // src/cli/index.ts
6130
- var VERSION = "0.11.2";
6211
+ var VERSION = "0.11.4";
6131
6212
  var HELP = `
6132
6213
  clawntenna v${VERSION}
6133
6214
  On-chain encrypted messaging for AI agents
package/dist/index.cjs CHANGED
@@ -771,7 +771,53 @@ var Clawntenna = class _Clawntenna {
771
771
  replyAuthor,
772
772
  mentions: options?.mentions
773
773
  });
774
- return this.registry.sendMessage(topicId, import_ethers.ethers.toUtf8Bytes(encrypted));
774
+ const txOverrides = {};
775
+ try {
776
+ const fee = await this.getTopicMessageFee(topicId);
777
+ if (fee.amount > BigInt(0)) {
778
+ const exempt = await this._isFeeExempt(topicId);
779
+ if (!exempt) {
780
+ if (fee.token === import_ethers.ethers.ZeroAddress) {
781
+ txOverrides.value = fee.amount;
782
+ } else {
783
+ const token = new import_ethers.ethers.Contract(fee.token, [
784
+ "function allowance(address,address) view returns (uint256)",
785
+ "function approve(address,uint256) returns (bool)"
786
+ ], this._signer);
787
+ const registryAddr = await this._registry.getAddress();
788
+ const allowance = await token.allowance(this._address, registryAddr);
789
+ if (allowance < fee.amount) {
790
+ const approveTx = await token.approve(registryAddr, fee.amount);
791
+ await approveTx.wait();
792
+ }
793
+ }
794
+ }
795
+ }
796
+ } catch {
797
+ }
798
+ return this.registry.sendMessage(topicId, import_ethers.ethers.toUtf8Bytes(encrypted), txOverrides);
799
+ }
800
+ /**
801
+ * Check if the current signer is exempt from message fees on a topic.
802
+ * Mirrors contract logic: topic owner, app owner, ROLE_ADMIN, PERMISSION_ADMIN.
803
+ */
804
+ async _isFeeExempt(topicId) {
805
+ const addr = this._address.toLowerCase();
806
+ const topic = await this.getTopic(topicId);
807
+ if (topic.owner.toLowerCase() === addr) return true;
808
+ const app = await this.getApplication(Number(topic.applicationId));
809
+ if (app.owner.toLowerCase() === addr) return true;
810
+ try {
811
+ const member = await this.getMember(Number(topic.applicationId), this._address);
812
+ if ((member.roles & 8 /* ADMIN */) !== 0) return true;
813
+ } catch {
814
+ }
815
+ try {
816
+ const perm = await this.getTopicPermission(topicId, this._address);
817
+ if (perm === 4 /* ADMIN */) return true;
818
+ } catch {
819
+ }
820
+ return false;
775
821
  }
776
822
  /**
777
823
  * Read and decrypt recent messages from a topic.
@@ -872,7 +918,28 @@ var Clawntenna = class _Clawntenna {
872
918
  // ===== TOPICS =====
873
919
  async createTopic(appId, name, description, accessLevel) {
874
920
  this.requireSigner();
875
- return this.registry.createTopic(appId, name, description, accessLevel);
921
+ const txOverrides = {};
922
+ try {
923
+ const app = await this.getApplication(appId);
924
+ const feeAmount = app.topicCreationFeeAmount ?? BigInt(0);
925
+ const feeToken = app.topicCreationFeeToken ?? import_ethers.ethers.ZeroAddress;
926
+ if (feeAmount > BigInt(0) && feeToken === import_ethers.ethers.ZeroAddress) {
927
+ txOverrides.value = feeAmount;
928
+ } else if (feeAmount > BigInt(0)) {
929
+ const token = new import_ethers.ethers.Contract(feeToken, [
930
+ "function allowance(address,address) view returns (uint256)",
931
+ "function approve(address,uint256) returns (bool)"
932
+ ], this._signer);
933
+ const registryAddr = await this._registry.getAddress();
934
+ const allowance = await token.allowance(this._address, registryAddr);
935
+ if (allowance < feeAmount) {
936
+ const approveTx = await token.approve(registryAddr, feeAmount);
937
+ await approveTx.wait();
938
+ }
939
+ }
940
+ } catch {
941
+ }
942
+ return this.registry.createTopic(appId, name, description, accessLevel, txOverrides);
876
943
  }
877
944
  async getTopic(topicId) {
878
945
  return this._wrapRpcError(async () => {