clawntenna 0.10.2 → 0.11.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/README.md CHANGED
@@ -222,7 +222,9 @@ const decimals = await client.getTokenDecimals('0xUSDC...'); // 6
222
222
 
223
223
  ### Escrow
224
224
 
225
- Message escrow holds fees until the topic owner responds, or refunds them after timeout. Supports both ERC-20 tokens and native ETH/AVAX (V9+).
225
+ Message escrow holds fees until the topic owner explicitly responds to specific deposits and releases them, or refunds them after timeout. Supports both ERC-20 tokens and native ETH/AVAX (V9+).
226
+
227
+ **V10 deposit-bound responses:** Topic owners must explicitly name which deposit(s) they're responding to. This creates an on-chain auditable binding — one reply does not release all deposits.
226
228
 
227
229
  ```ts
228
230
  // Enable escrow on a topic (topic owner only)
@@ -243,6 +245,16 @@ const status = await client.getDepositStatus(depositId);
243
245
  // List pending deposits for a topic
244
246
  const pendingIds = await client.getPendingDeposits(topicId);
245
247
 
248
+ // Step 1: Respond to specific deposits (sends message + binds to deposit IDs)
249
+ await client.respondToDeposits(topicId, '0xpayload', [depositId1, depositId2]);
250
+
251
+ // Step 2: Check response status
252
+ const responded = await client.hasResponse(depositId); // boolean
253
+
254
+ // Step 3: Release responded deposits (topic owner only)
255
+ await client.releaseDeposit(depositId, 0); // messageRef is optional (0 = none)
256
+ await client.batchReleaseDeposits([1, 2, 3]);
257
+
246
258
  // Refunds (sender only, after timeout)
247
259
  const canRefund = await client.canClaimRefund(depositId);
248
260
  await client.claimRefund(depositId);
package/dist/cli/index.js CHANGED
@@ -104,6 +104,8 @@ var REGISTRY_ABI = [
104
104
  "function setNicknameCooldown(uint256 appId, uint256 cooldownSeconds)",
105
105
  // Messaging
106
106
  "function sendMessage(uint256 topicId, bytes payload)",
107
+ // V10 — deposit-bound response
108
+ "function respondToDeposits(uint256 topicId, bytes payload, uint256[] depositIds)",
107
109
  // Fees
108
110
  "function setTopicCreationFee(uint256 appId, address feeTokenAddr, uint256 feeAmount)",
109
111
  "function setTopicMessageFee(uint256 topicId, address feeTokenAddr, uint256 feeAmount)",
@@ -193,17 +195,28 @@ var ESCROW_ABI = [
193
195
  "function getDepositStatus(uint256 depositId) view returns (uint8)",
194
196
  "function getPendingDeposits(uint256 topicId) view returns (uint256[])",
195
197
  "function canClaimRefund(uint256 depositId) view returns (bool)",
198
+ // V3
199
+ "function depositMessageRef(uint256 depositId) view returns (uint256)",
200
+ "function getDepositMessageRef(uint256 depositId) view returns (uint256)",
201
+ "function depositResponseRecorded(uint256 depositId) view returns (bool)",
202
+ "function hasResponse(uint256 depositId) view returns (bool)",
196
203
  // ===== WRITE FUNCTIONS =====
197
204
  "function enableEscrow(uint256 topicId, uint64 timeoutSeconds)",
198
205
  "function disableEscrow(uint256 topicId)",
199
206
  "function claimRefund(uint256 depositId)",
200
207
  "function batchClaimRefunds(uint256[] depositIds)",
208
+ // V3 — per-deposit release
209
+ "function releaseDeposit(uint256 depositId, uint256 messageRef)",
210
+ "function batchReleaseDeposits(uint256[] depositIds, uint256[] messageRefs)",
201
211
  // ===== EVENTS =====
202
212
  "event EscrowEnabled(uint256 indexed topicId, uint64 timeout)",
203
213
  "event EscrowDisabled(uint256 indexed topicId)",
204
214
  "event DepositRecorded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)",
205
215
  "event DepositReleased(uint256 indexed depositId, uint256 indexed topicId, uint256 recipientAmount, uint256 appOwnerAmount, uint256 platformAmount)",
206
- "event DepositRefunded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)"
216
+ "event DepositRefunded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)",
217
+ // V3
218
+ "event DepositReleasedByOwner(uint256 indexed depositId, uint256 indexed topicId, address indexed releasedBy, uint256 messageRef)",
219
+ "event DepositResponseRecorded(uint256 indexed depositId, uint256 indexed topicId, address indexed respondedBy)"
207
220
  ];
208
221
  var KEY_MANAGER_ABI = [
209
222
  // ===== READ FUNCTIONS =====
@@ -3913,6 +3926,50 @@ var Clawntenna = class _Clawntenna {
3913
3926
  this.requireSigner();
3914
3927
  return this.requireEscrow().batchClaimRefunds(depositIds);
3915
3928
  }
3929
+ /**
3930
+ * Respond to specific deposits by sending a message and binding it on-chain (topic owner only).
3931
+ * This creates an auditable record: "at block X, owner sent message Y for deposits [a, b, c]".
3932
+ * Each deposit must have a recorded response before it can be released.
3933
+ * @param topicId Topic ID
3934
+ * @param payload Message payload (encrypted response)
3935
+ * @param depositIds Array of deposit IDs being responded to
3936
+ */
3937
+ async respondToDeposits(topicId, payload, depositIds) {
3938
+ this.requireSigner();
3939
+ return this.registry.respondToDeposits(topicId, payload, depositIds);
3940
+ }
3941
+ /**
3942
+ * Release a single escrow deposit (topic owner only).
3943
+ * Requires a prior respondToDeposits() call for this deposit.
3944
+ * @param depositId Deposit ID to release
3945
+ * @param messageRef Optional off-chain message reference (default 0)
3946
+ */
3947
+ async releaseDeposit(depositId, messageRef = 0) {
3948
+ this.requireSigner();
3949
+ return this.requireEscrow().releaseDeposit(depositId, messageRef);
3950
+ }
3951
+ /**
3952
+ * Batch release escrow deposits (topic owner only, max 50).
3953
+ * Requires a prior respondToDeposits() call for each deposit.
3954
+ * @param depositIds Array of deposit IDs to release
3955
+ * @param messageRefs Optional array of off-chain references (empty or same length)
3956
+ */
3957
+ async batchReleaseDeposits(depositIds, messageRefs = []) {
3958
+ this.requireSigner();
3959
+ return this.requireEscrow().batchReleaseDeposits(depositIds, messageRefs);
3960
+ }
3961
+ /**
3962
+ * Get the message reference for a released deposit.
3963
+ */
3964
+ async getDepositMessageRef(depositId) {
3965
+ return this.requireEscrow().getDepositMessageRef(depositId);
3966
+ }
3967
+ /**
3968
+ * Check if a deposit has a recorded owner response.
3969
+ */
3970
+ async hasResponse(depositId) {
3971
+ return this.requireEscrow().hasResponse(depositId);
3972
+ }
3916
3973
  /**
3917
3974
  * Parse a transaction receipt to extract the depositId from a DepositRecorded event.
3918
3975
  * Returns null if no DepositRecorded event is found (e.g. no escrow on this tx).
@@ -5667,6 +5724,46 @@ async function escrowRefundBatch(depositIds, flags) {
5667
5724
  console.log(`Confirmed in block ${receipt?.blockNumber}`);
5668
5725
  }
5669
5726
  }
5727
+ async function escrowRespond(topicId, depositIds, payload, flags) {
5728
+ const client = loadClient(flags);
5729
+ const json = flags.json ?? false;
5730
+ if (!json) console.log(`Responding to deposit(s) [${depositIds.join(", ")}] on topic #${topicId}...`);
5731
+ const tx = await client.respondToDeposits(topicId, payload, depositIds);
5732
+ const receipt = await tx.wait();
5733
+ if (json) {
5734
+ output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, topicId, depositIds }, true);
5735
+ } else {
5736
+ console.log(`TX: ${tx.hash}`);
5737
+ console.log(`Confirmed in block ${receipt?.blockNumber}`);
5738
+ console.log(`Responded to ${depositIds.length} deposit(s). They can now be released.`);
5739
+ }
5740
+ }
5741
+ async function escrowRelease(depositId, messageRef, flags) {
5742
+ const client = loadClient(flags);
5743
+ const json = flags.json ?? false;
5744
+ if (!json) console.log(`Releasing deposit #${depositId}${messageRef ? ` (ref: ${messageRef})` : ""}...`);
5745
+ const tx = await client.releaseDeposit(depositId, messageRef);
5746
+ const receipt = await tx.wait();
5747
+ if (json) {
5748
+ output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, depositId, messageRef }, true);
5749
+ } else {
5750
+ console.log(`TX: ${tx.hash}`);
5751
+ console.log(`Confirmed in block ${receipt?.blockNumber}`);
5752
+ }
5753
+ }
5754
+ async function escrowReleaseBatch(depositIds, flags) {
5755
+ const client = loadClient(flags);
5756
+ const json = flags.json ?? false;
5757
+ if (!json) console.log(`Releasing ${depositIds.length} deposits...`);
5758
+ const tx = await client.batchReleaseDeposits(depositIds);
5759
+ const receipt = await tx.wait();
5760
+ if (json) {
5761
+ output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, depositIds }, true);
5762
+ } else {
5763
+ console.log(`TX: ${tx.hash}`);
5764
+ console.log(`Confirmed in block ${receipt?.blockNumber}`);
5765
+ }
5766
+ }
5670
5767
 
5671
5768
  // src/cli/errors.ts
5672
5769
  var ERROR_MAP = {
@@ -5718,7 +5815,7 @@ function decodeContractError(err) {
5718
5815
  }
5719
5816
 
5720
5817
  // src/cli/index.ts
5721
- var VERSION = "0.10.2";
5818
+ var VERSION = "0.11.0";
5722
5819
  var HELP = `
5723
5820
  clawntenna v${VERSION}
5724
5821
  On-chain encrypted messaging for AI agents
@@ -5798,6 +5895,9 @@ var HELP = `
5798
5895
  escrow status <topicId> Show escrow config
5799
5896
  escrow deposits <topicId> List pending deposits
5800
5897
  escrow deposit <depositId> Show deposit info
5898
+ escrow respond <topicId> <id1> [id2...] --payload 0x Respond to deposits (topic owner)
5899
+ escrow release <depositId> [--ref N] Release deposit (topic owner)
5900
+ escrow release-batch <id1> <id2> ... Batch release deposits
5801
5901
  escrow refund <depositId> Claim refund
5802
5902
  escrow refund-batch <id1> <id2> ... Batch refund
5803
5903
 
@@ -6199,6 +6299,22 @@ async function main() {
6199
6299
  const depositId = parseInt(args[1], 10);
6200
6300
  if (isNaN(depositId)) outputError("Usage: clawntenna escrow deposit <depositId>", json);
6201
6301
  await escrowDeposit(depositId, cf);
6302
+ } else if (sub === "respond") {
6303
+ const topicId = parseInt(args[1], 10);
6304
+ if (isNaN(topicId)) outputError("Usage: clawntenna escrow respond <topicId> <id1> [id2...] --payload 0x...", json);
6305
+ const depositIds = args.slice(2).map((a) => parseInt(a, 10));
6306
+ if (depositIds.length === 0 || depositIds.some(isNaN)) outputError("Usage: clawntenna escrow respond <topicId> <id1> [id2...] --payload 0x...", json);
6307
+ const payload = flags.payload || "0x";
6308
+ await escrowRespond(topicId, depositIds, payload, cf);
6309
+ } else if (sub === "release") {
6310
+ const depositId = parseInt(args[1], 10);
6311
+ if (isNaN(depositId)) outputError("Usage: clawntenna escrow release <depositId> [--ref N]", json);
6312
+ const messageRef = flags.ref ? parseInt(flags.ref, 10) : 0;
6313
+ await escrowRelease(depositId, messageRef, cf);
6314
+ } else if (sub === "release-batch") {
6315
+ const ids = args.slice(1).map((a) => parseInt(a, 10));
6316
+ if (ids.length === 0 || ids.some(isNaN)) outputError("Usage: clawntenna escrow release-batch <id1> <id2> ...", json);
6317
+ await escrowReleaseBatch(ids, cf);
6202
6318
  } else if (sub === "refund") {
6203
6319
  const depositId = parseInt(args[1], 10);
6204
6320
  if (isNaN(depositId)) outputError("Usage: clawntenna escrow refund <depositId>", json);
@@ -6208,7 +6324,7 @@ async function main() {
6208
6324
  if (ids.length === 0 || ids.some(isNaN)) outputError("Usage: clawntenna escrow refund-batch <id1> <id2> ...", json);
6209
6325
  await escrowRefundBatch(ids, cf);
6210
6326
  } else {
6211
- outputError(`Unknown escrow subcommand: ${sub}. Use: enable, disable, status, deposits, deposit, refund, refund-batch`, json);
6327
+ outputError(`Unknown escrow subcommand: ${sub}. Use: enable, disable, status, deposits, deposit, release, release-batch, refund, refund-batch`, json);
6212
6328
  }
6213
6329
  break;
6214
6330
  }
package/dist/index.cjs CHANGED
@@ -190,6 +190,8 @@ var REGISTRY_ABI = [
190
190
  "function setNicknameCooldown(uint256 appId, uint256 cooldownSeconds)",
191
191
  // Messaging
192
192
  "function sendMessage(uint256 topicId, bytes payload)",
193
+ // V10 — deposit-bound response
194
+ "function respondToDeposits(uint256 topicId, bytes payload, uint256[] depositIds)",
193
195
  // Fees
194
196
  "function setTopicCreationFee(uint256 appId, address feeTokenAddr, uint256 feeAmount)",
195
197
  "function setTopicMessageFee(uint256 topicId, address feeTokenAddr, uint256 feeAmount)",
@@ -279,17 +281,28 @@ var ESCROW_ABI = [
279
281
  "function getDepositStatus(uint256 depositId) view returns (uint8)",
280
282
  "function getPendingDeposits(uint256 topicId) view returns (uint256[])",
281
283
  "function canClaimRefund(uint256 depositId) view returns (bool)",
284
+ // V3
285
+ "function depositMessageRef(uint256 depositId) view returns (uint256)",
286
+ "function getDepositMessageRef(uint256 depositId) view returns (uint256)",
287
+ "function depositResponseRecorded(uint256 depositId) view returns (bool)",
288
+ "function hasResponse(uint256 depositId) view returns (bool)",
282
289
  // ===== WRITE FUNCTIONS =====
283
290
  "function enableEscrow(uint256 topicId, uint64 timeoutSeconds)",
284
291
  "function disableEscrow(uint256 topicId)",
285
292
  "function claimRefund(uint256 depositId)",
286
293
  "function batchClaimRefunds(uint256[] depositIds)",
294
+ // V3 — per-deposit release
295
+ "function releaseDeposit(uint256 depositId, uint256 messageRef)",
296
+ "function batchReleaseDeposits(uint256[] depositIds, uint256[] messageRefs)",
287
297
  // ===== EVENTS =====
288
298
  "event EscrowEnabled(uint256 indexed topicId, uint64 timeout)",
289
299
  "event EscrowDisabled(uint256 indexed topicId)",
290
300
  "event DepositRecorded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)",
291
301
  "event DepositReleased(uint256 indexed depositId, uint256 indexed topicId, uint256 recipientAmount, uint256 appOwnerAmount, uint256 platformAmount)",
292
- "event DepositRefunded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)"
302
+ "event DepositRefunded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)",
303
+ // V3
304
+ "event DepositReleasedByOwner(uint256 indexed depositId, uint256 indexed topicId, address indexed releasedBy, uint256 messageRef)",
305
+ "event DepositResponseRecorded(uint256 indexed depositId, uint256 indexed topicId, address indexed respondedBy)"
293
306
  ];
294
307
  var KEY_MANAGER_ABI = [
295
308
  // ===== READ FUNCTIONS =====
@@ -1113,6 +1126,50 @@ var Clawntenna = class _Clawntenna {
1113
1126
  this.requireSigner();
1114
1127
  return this.requireEscrow().batchClaimRefunds(depositIds);
1115
1128
  }
1129
+ /**
1130
+ * Respond to specific deposits by sending a message and binding it on-chain (topic owner only).
1131
+ * This creates an auditable record: "at block X, owner sent message Y for deposits [a, b, c]".
1132
+ * Each deposit must have a recorded response before it can be released.
1133
+ * @param topicId Topic ID
1134
+ * @param payload Message payload (encrypted response)
1135
+ * @param depositIds Array of deposit IDs being responded to
1136
+ */
1137
+ async respondToDeposits(topicId, payload, depositIds) {
1138
+ this.requireSigner();
1139
+ return this.registry.respondToDeposits(topicId, payload, depositIds);
1140
+ }
1141
+ /**
1142
+ * Release a single escrow deposit (topic owner only).
1143
+ * Requires a prior respondToDeposits() call for this deposit.
1144
+ * @param depositId Deposit ID to release
1145
+ * @param messageRef Optional off-chain message reference (default 0)
1146
+ */
1147
+ async releaseDeposit(depositId, messageRef = 0) {
1148
+ this.requireSigner();
1149
+ return this.requireEscrow().releaseDeposit(depositId, messageRef);
1150
+ }
1151
+ /**
1152
+ * Batch release escrow deposits (topic owner only, max 50).
1153
+ * Requires a prior respondToDeposits() call for each deposit.
1154
+ * @param depositIds Array of deposit IDs to release
1155
+ * @param messageRefs Optional array of off-chain references (empty or same length)
1156
+ */
1157
+ async batchReleaseDeposits(depositIds, messageRefs = []) {
1158
+ this.requireSigner();
1159
+ return this.requireEscrow().batchReleaseDeposits(depositIds, messageRefs);
1160
+ }
1161
+ /**
1162
+ * Get the message reference for a released deposit.
1163
+ */
1164
+ async getDepositMessageRef(depositId) {
1165
+ return this.requireEscrow().getDepositMessageRef(depositId);
1166
+ }
1167
+ /**
1168
+ * Check if a deposit has a recorded owner response.
1169
+ */
1170
+ async hasResponse(depositId) {
1171
+ return this.requireEscrow().hasResponse(depositId);
1172
+ }
1116
1173
  /**
1117
1174
  * Parse a transaction receipt to extract the depositId from a DepositRecorded event.
1118
1175
  * Returns null if no DepositRecorded event is found (e.g. no escrow on this tx).