clawntenna 0.11.1 → 0.11.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/cli/index.js CHANGED
@@ -216,7 +216,14 @@ var ESCROW_ABI = [
216
216
  "event DepositRefunded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)",
217
217
  // V3
218
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)"
219
+ "event DepositResponseRecorded(uint256 indexed depositId, uint256 indexed topicId, address indexed respondedBy)",
220
+ // V4 — on-chain accumulators + credibility
221
+ "function getRecipientStats(address wallet) view returns (tuple(uint64 depositsReceived, uint64 depositsReleased, uint64 depositsRefunded, uint64 depositsExpired))",
222
+ "function getResponseRate(address wallet) view returns (uint256)",
223
+ "function getCredibility(address wallet) view returns (uint256 responseRate, uint64 depositsReceived, uint64 depositsReleased, uint64 depositsRefunded, uint256 totalEarned, uint256 totalRefunded)",
224
+ "function amountEarned(address wallet) view returns (uint256)",
225
+ "function amountRefunded(address wallet) view returns (uint256)",
226
+ "event RecipientStatsUpdated(address indexed wallet, uint64 received, uint64 released, uint64 refunded)"
220
227
  ];
221
228
  var KEY_MANAGER_ABI = [
222
229
  // ===== READ FUNCTIONS =====
@@ -3970,6 +3977,63 @@ var Clawntenna = class _Clawntenna {
3970
3977
  async hasResponse(depositId) {
3971
3978
  return this.requireEscrow().hasResponse(depositId);
3972
3979
  }
3980
+ /**
3981
+ * Get lifetime escrow stats for a wallet (V4).
3982
+ */
3983
+ async getRecipientStats(wallet) {
3984
+ const s = await this.requireEscrow().getRecipientStats(wallet);
3985
+ return {
3986
+ depositsReceived: BigInt(s.depositsReceived),
3987
+ depositsReleased: BigInt(s.depositsReleased),
3988
+ depositsRefunded: BigInt(s.depositsRefunded),
3989
+ depositsExpired: BigInt(s.depositsExpired)
3990
+ };
3991
+ }
3992
+ /**
3993
+ * Get credibility snapshot for a wallet (V4).
3994
+ * Returns response rate as 0-100 percentage and lifetime escrow totals.
3995
+ */
3996
+ async getWalletCredibility(wallet) {
3997
+ const escrow = this.requireEscrow();
3998
+ const cred = await escrow.getCredibility(wallet);
3999
+ const responseRate = Number(cred.responseRate) / 100;
4000
+ let formattedEarned = null;
4001
+ let formattedRefunded = null;
4002
+ try {
4003
+ formattedEarned = ethers.formatEther(cred.totalEarned);
4004
+ formattedRefunded = ethers.formatEther(cred.totalRefunded);
4005
+ } catch {
4006
+ }
4007
+ return {
4008
+ responseRate,
4009
+ depositsReceived: BigInt(cred.depositsReceived),
4010
+ depositsReleased: BigInt(cred.depositsReleased),
4011
+ depositsRefunded: BigInt(cred.depositsRefunded),
4012
+ totalEarned: BigInt(cred.totalEarned),
4013
+ totalRefunded: BigInt(cred.totalRefunded),
4014
+ formattedEarned,
4015
+ formattedRefunded
4016
+ };
4017
+ }
4018
+ /**
4019
+ * Get the on-chain total amount earned by a wallet via escrow releases (V4).
4020
+ */
4021
+ async getAmountEarned(wallet) {
4022
+ return this.requireEscrow().amountEarned(wallet);
4023
+ }
4024
+ /**
4025
+ * Get the on-chain total amount lost to refunds for a wallet (V4).
4026
+ */
4027
+ async getAmountRefunded(wallet) {
4028
+ return this.requireEscrow().amountRefunded(wallet);
4029
+ }
4030
+ /**
4031
+ * Get response rate for a wallet as basis points (0-10000) (V4).
4032
+ */
4033
+ async getResponseRate(wallet) {
4034
+ const rate = await this.requireEscrow().getResponseRate(wallet);
4035
+ return Number(rate);
4036
+ }
3973
4037
  /**
3974
4038
  * Parse a transaction receipt to extract the depositId from a DepositRecorded event.
3975
4039
  * Returns null if no DepositRecorded event is found (e.g. no escrow on this tx).
@@ -4026,6 +4090,22 @@ var Clawntenna = class _Clawntenna {
4026
4090
  canClaim
4027
4091
  };
4028
4092
  }
4093
+ // ===== PRIVATE HELPERS =====
4094
+ /**
4095
+ * Query contract events in chunked ranges to stay within public RPC limits.
4096
+ */
4097
+ async _queryFilterChunked(contract, filter, fromBlock, toBlock, chunkSize = 1e4) {
4098
+ const results = [];
4099
+ for (let start = fromBlock; start <= toBlock; start += chunkSize) {
4100
+ const end = Math.min(start + chunkSize - 1, toBlock);
4101
+ const chunk = await this._wrapRpcError(
4102
+ () => contract.queryFilter(filter, start, end),
4103
+ "queryFilterChunked"
4104
+ );
4105
+ results.push(...chunk);
4106
+ }
4107
+ return results;
4108
+ }
4029
4109
  // ===== ESCROW INBOX (deposit → message bridge) =====
4030
4110
  /**
4031
4111
  * Reverse lookup: find the transaction hash that created a deposit.
@@ -4037,10 +4117,7 @@ var Clawntenna = class _Clawntenna {
4037
4117
  const currentBlock = await this.provider.getBlockNumber();
4038
4118
  const startBlock = currentBlock - chain.defaultLookback;
4039
4119
  const filter = escrow.filters.DepositRecorded(depositId);
4040
- const events = await this._wrapRpcError(
4041
- () => escrow.queryFilter(filter, startBlock, currentBlock),
4042
- "getDepositTxHash"
4043
- );
4120
+ const events = await this._queryFilterChunked(escrow, filter, startBlock, currentBlock);
4044
4121
  if (events.length === 0) return null;
4045
4122
  return events[0].transactionHash;
4046
4123
  }
@@ -4072,10 +4149,7 @@ var Clawntenna = class _Clawntenna {
4072
4149
  const currentBlock = await this.provider.getBlockNumber();
4073
4150
  const startBlock = currentBlock - chain.defaultLookback;
4074
4151
  const topicFilter = escrow.filters.DepositRecorded(null, topicId);
4075
- const events = await this._wrapRpcError(
4076
- () => escrow.queryFilter(topicFilter, startBlock, currentBlock),
4077
- "getEscrowInbox"
4078
- );
4152
+ const events = await this._queryFilterChunked(escrow, topicFilter, startBlock, currentBlock);
4079
4153
  const txHashMap = /* @__PURE__ */ new Map();
4080
4154
  for (const evt of events) {
4081
4155
  const id = evt.args.depositId.toString();
@@ -5966,6 +6040,35 @@ Topic #${topicId} inbox (${inbox.length} pending):
5966
6040
  console.log();
5967
6041
  }
5968
6042
  }
6043
+ async function escrowStats(address, flags) {
6044
+ const client = loadClient(flags, false);
6045
+ const json = flags.json ?? false;
6046
+ const cred = await client.getWalletCredibility(address);
6047
+ if (json) {
6048
+ output({
6049
+ address,
6050
+ responseRate: cred.responseRate,
6051
+ depositsReceived: cred.depositsReceived.toString(),
6052
+ depositsReleased: cred.depositsReleased.toString(),
6053
+ depositsRefunded: cred.depositsRefunded.toString(),
6054
+ totalEarned: cred.totalEarned.toString(),
6055
+ totalRefunded: cred.totalRefunded.toString(),
6056
+ formattedEarned: cred.formattedEarned,
6057
+ formattedRefunded: cred.formattedRefunded
6058
+ }, true);
6059
+ return;
6060
+ }
6061
+ const shortAddr = address.slice(0, 6) + "..." + address.slice(-4);
6062
+ console.log(`
6063
+ Escrow stats for ${shortAddr}:
6064
+ `);
6065
+ console.log(` Response rate: ${cred.responseRate.toFixed(1)}%`);
6066
+ console.log(` Deposits received: ${cred.depositsReceived}`);
6067
+ console.log(` Released: ${cred.depositsReleased} | Refunded: ${cred.depositsRefunded}`);
6068
+ console.log(` Total earned: ${cred.formattedEarned ?? cred.totalEarned.toString()} ETH`);
6069
+ console.log(` Total refunded: ${cred.formattedRefunded ?? cred.totalRefunded.toString()} ETH`);
6070
+ console.log();
6071
+ }
5969
6072
  function formatAgo(epochSeconds) {
5970
6073
  const diff = Math.floor(Date.now() / 1e3) - epochSeconds;
5971
6074
  if (diff < 60) return `${diff}s ago`;
@@ -6024,7 +6127,7 @@ function decodeContractError(err) {
6024
6127
  }
6025
6128
 
6026
6129
  // src/cli/index.ts
6027
- var VERSION = "0.11.1";
6130
+ var VERSION = "0.11.2";
6028
6131
  var HELP = `
6029
6132
  clawntenna v${VERSION}
6030
6133
  On-chain encrypted messaging for AI agents
@@ -6108,6 +6211,7 @@ var HELP = `
6108
6211
  escrow respond <topicId> <id1> [id2...] --payload 0x Respond to deposits (topic owner)
6109
6212
  escrow release <depositId> [--ref N] Release deposit (topic owner)
6110
6213
  escrow release-batch <id1> <id2> ... Batch release deposits
6214
+ escrow stats <address> Show wallet credibility & escrow stats
6111
6215
  escrow refund <depositId> Claim refund
6112
6216
  escrow refund-batch <id1> <id2> ... Batch refund
6113
6217
 
@@ -6537,8 +6641,12 @@ async function main() {
6537
6641
  const ids = args.slice(1).map((a) => parseInt(a, 10));
6538
6642
  if (ids.length === 0 || ids.some(isNaN)) outputError("Usage: clawntenna escrow refund-batch <id1> <id2> ...", json);
6539
6643
  await escrowRefundBatch(ids, cf);
6644
+ } else if (sub === "stats") {
6645
+ const address = args[1];
6646
+ if (!address) outputError("Usage: clawntenna escrow stats <address>", json);
6647
+ await escrowStats(address, cf);
6540
6648
  } else {
6541
- outputError(`Unknown escrow subcommand: ${sub}. Use: inbox, enable, disable, status, deposits, deposit, respond, release, release-batch, refund, refund-batch`, json);
6649
+ outputError(`Unknown escrow subcommand: ${sub}. Use: inbox, enable, disable, status, stats, deposits, deposit, respond, release, release-batch, refund, refund-batch`, json);
6542
6650
  }
6543
6651
  break;
6544
6652
  }
package/dist/index.cjs CHANGED
@@ -302,7 +302,14 @@ var ESCROW_ABI = [
302
302
  "event DepositRefunded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)",
303
303
  // V3
304
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)"
305
+ "event DepositResponseRecorded(uint256 indexed depositId, uint256 indexed topicId, address indexed respondedBy)",
306
+ // V4 — on-chain accumulators + credibility
307
+ "function getRecipientStats(address wallet) view returns (tuple(uint64 depositsReceived, uint64 depositsReleased, uint64 depositsRefunded, uint64 depositsExpired))",
308
+ "function getResponseRate(address wallet) view returns (uint256)",
309
+ "function getCredibility(address wallet) view returns (uint256 responseRate, uint64 depositsReceived, uint64 depositsReleased, uint64 depositsRefunded, uint256 totalEarned, uint256 totalRefunded)",
310
+ "function amountEarned(address wallet) view returns (uint256)",
311
+ "function amountRefunded(address wallet) view returns (uint256)",
312
+ "event RecipientStatsUpdated(address indexed wallet, uint64 received, uint64 released, uint64 refunded)"
306
313
  ];
307
314
  var KEY_MANAGER_ABI = [
308
315
  // ===== READ FUNCTIONS =====
@@ -1170,6 +1177,63 @@ var Clawntenna = class _Clawntenna {
1170
1177
  async hasResponse(depositId) {
1171
1178
  return this.requireEscrow().hasResponse(depositId);
1172
1179
  }
1180
+ /**
1181
+ * Get lifetime escrow stats for a wallet (V4).
1182
+ */
1183
+ async getRecipientStats(wallet) {
1184
+ const s = await this.requireEscrow().getRecipientStats(wallet);
1185
+ return {
1186
+ depositsReceived: BigInt(s.depositsReceived),
1187
+ depositsReleased: BigInt(s.depositsReleased),
1188
+ depositsRefunded: BigInt(s.depositsRefunded),
1189
+ depositsExpired: BigInt(s.depositsExpired)
1190
+ };
1191
+ }
1192
+ /**
1193
+ * Get credibility snapshot for a wallet (V4).
1194
+ * Returns response rate as 0-100 percentage and lifetime escrow totals.
1195
+ */
1196
+ async getWalletCredibility(wallet) {
1197
+ const escrow = this.requireEscrow();
1198
+ const cred = await escrow.getCredibility(wallet);
1199
+ const responseRate = Number(cred.responseRate) / 100;
1200
+ let formattedEarned = null;
1201
+ let formattedRefunded = null;
1202
+ try {
1203
+ formattedEarned = import_ethers.ethers.formatEther(cred.totalEarned);
1204
+ formattedRefunded = import_ethers.ethers.formatEther(cred.totalRefunded);
1205
+ } catch {
1206
+ }
1207
+ return {
1208
+ responseRate,
1209
+ depositsReceived: BigInt(cred.depositsReceived),
1210
+ depositsReleased: BigInt(cred.depositsReleased),
1211
+ depositsRefunded: BigInt(cred.depositsRefunded),
1212
+ totalEarned: BigInt(cred.totalEarned),
1213
+ totalRefunded: BigInt(cred.totalRefunded),
1214
+ formattedEarned,
1215
+ formattedRefunded
1216
+ };
1217
+ }
1218
+ /**
1219
+ * Get the on-chain total amount earned by a wallet via escrow releases (V4).
1220
+ */
1221
+ async getAmountEarned(wallet) {
1222
+ return this.requireEscrow().amountEarned(wallet);
1223
+ }
1224
+ /**
1225
+ * Get the on-chain total amount lost to refunds for a wallet (V4).
1226
+ */
1227
+ async getAmountRefunded(wallet) {
1228
+ return this.requireEscrow().amountRefunded(wallet);
1229
+ }
1230
+ /**
1231
+ * Get response rate for a wallet as basis points (0-10000) (V4).
1232
+ */
1233
+ async getResponseRate(wallet) {
1234
+ const rate = await this.requireEscrow().getResponseRate(wallet);
1235
+ return Number(rate);
1236
+ }
1173
1237
  /**
1174
1238
  * Parse a transaction receipt to extract the depositId from a DepositRecorded event.
1175
1239
  * Returns null if no DepositRecorded event is found (e.g. no escrow on this tx).
@@ -1226,6 +1290,22 @@ var Clawntenna = class _Clawntenna {
1226
1290
  canClaim
1227
1291
  };
1228
1292
  }
1293
+ // ===== PRIVATE HELPERS =====
1294
+ /**
1295
+ * Query contract events in chunked ranges to stay within public RPC limits.
1296
+ */
1297
+ async _queryFilterChunked(contract, filter, fromBlock, toBlock, chunkSize = 1e4) {
1298
+ const results = [];
1299
+ for (let start = fromBlock; start <= toBlock; start += chunkSize) {
1300
+ const end = Math.min(start + chunkSize - 1, toBlock);
1301
+ const chunk = await this._wrapRpcError(
1302
+ () => contract.queryFilter(filter, start, end),
1303
+ "queryFilterChunked"
1304
+ );
1305
+ results.push(...chunk);
1306
+ }
1307
+ return results;
1308
+ }
1229
1309
  // ===== ESCROW INBOX (deposit → message bridge) =====
1230
1310
  /**
1231
1311
  * Reverse lookup: find the transaction hash that created a deposit.
@@ -1237,10 +1317,7 @@ var Clawntenna = class _Clawntenna {
1237
1317
  const currentBlock = await this.provider.getBlockNumber();
1238
1318
  const startBlock = currentBlock - chain.defaultLookback;
1239
1319
  const filter = escrow.filters.DepositRecorded(depositId);
1240
- const events = await this._wrapRpcError(
1241
- () => escrow.queryFilter(filter, startBlock, currentBlock),
1242
- "getDepositTxHash"
1243
- );
1320
+ const events = await this._queryFilterChunked(escrow, filter, startBlock, currentBlock);
1244
1321
  if (events.length === 0) return null;
1245
1322
  return events[0].transactionHash;
1246
1323
  }
@@ -1272,10 +1349,7 @@ var Clawntenna = class _Clawntenna {
1272
1349
  const currentBlock = await this.provider.getBlockNumber();
1273
1350
  const startBlock = currentBlock - chain.defaultLookback;
1274
1351
  const topicFilter = escrow.filters.DepositRecorded(null, topicId);
1275
- const events = await this._wrapRpcError(
1276
- () => escrow.queryFilter(topicFilter, startBlock, currentBlock),
1277
- "getEscrowInbox"
1278
- );
1352
+ const events = await this._queryFilterChunked(escrow, topicFilter, startBlock, currentBlock);
1279
1353
  const txHashMap = /* @__PURE__ */ new Map();
1280
1354
  for (const evt of events) {
1281
1355
  const id = evt.args.depositId.toString();