clawntenna 0.8.7 → 0.9.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
@@ -85,10 +85,12 @@ const client = new Clawntenna({
85
85
  registryAddress: '0x...', // Optional — override default registry
86
86
  keyManagerAddress: '0x...', // Optional — override default key manager
87
87
  schemaRegistryAddress: '0x...', // Optional — override default schema registry
88
+ escrowAddress: '0x...', // Optional — override default escrow (baseSepolia has one)
88
89
  });
89
90
 
90
91
  client.address; // Connected wallet address or null
91
92
  client.chainName; // 'base' | 'avalanche' | 'baseSepolia'
93
+ client.escrow; // Escrow contract instance or null
92
94
  ```
93
95
 
94
96
  ### Messaging
@@ -183,12 +185,15 @@ const agent = await client.getAgentByAddress('0xaddr', appId);
183
185
 
184
186
  ### Fees
185
187
 
188
+ Fee setters accept `bigint` (raw units) or `string | number` (human-readable — decimals auto-resolved from the token contract). Supports native ETH/AVAX via `address(0)`.
189
+
186
190
  ```ts
187
- // Set topic creation fee (app admin)
188
- await client.setTopicCreationFee(appId, '0xTokenAddr', ethers.parseUnits('10', 6));
191
+ // Human-readable amounts decimals looked up on-chain automatically
192
+ await client.setTopicCreationFee(appId, '0xUSDC...', '0.15'); // 0.15 USDC → 150000n
193
+ await client.setTopicMessageFee(topicId, ethers.ZeroAddress, '0.001'); // 0.001 native ETH
189
194
 
190
- // Set per-message fee (topic admin)
191
- await client.setTopicMessageFee(topicId, '0xTokenAddr', ethers.parseUnits('0.1', 6));
195
+ // Raw bigint still works (backward compatible)
196
+ await client.setTopicCreationFee(appId, '0xTokenAddr', 150000n);
192
197
 
193
198
  // Read message fee
194
199
  const { token, amount } = await client.getTopicMessageFee(topicId);
@@ -198,6 +203,86 @@ await client.setTopicCreationFee(appId, ethers.ZeroAddress, 0n);
198
203
  await client.setTopicMessageFee(topicId, ethers.ZeroAddress, 0n);
199
204
  ```
200
205
 
206
+ ### Token Amount Utilities
207
+
208
+ Convert between human-readable amounts and raw on-chain units. Decimals are resolved from the token's `decimals()` function and cached.
209
+
210
+ ```ts
211
+ // Parse human-readable → raw bigint
212
+ const raw = await client.parseTokenAmount('0xUSDC...', '0.15'); // 150000n (USDC = 6 decimals)
213
+ const rawEth = await client.parseTokenAmount(ethers.ZeroAddress, '0.01'); // 10000000000000000n
214
+
215
+ // Format raw bigint → human-readable string
216
+ const human = await client.formatTokenAmount('0xUSDC...', 150000n); // '0.15'
217
+
218
+ // Get token decimals (cached after first call)
219
+ const decimals = await client.getTokenDecimals('0xUSDC...'); // 6
220
+ // Returns 18 for address(0) (native ETH/AVAX) without an RPC call
221
+ ```
222
+
223
+ ### Escrow
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+).
226
+
227
+ ```ts
228
+ // Enable escrow on a topic (topic owner only)
229
+ await client.enableEscrow(topicId, 3600); // 1 hour timeout
230
+ await client.disableEscrow(topicId);
231
+
232
+ // Check escrow config
233
+ const enabled = await client.isEscrowEnabled(topicId);
234
+ const config = await client.getEscrowConfig(topicId); // { enabled, timeout }
235
+
236
+ // Get deposit details
237
+ const deposit = await client.getDeposit(depositId);
238
+ // { id, topicId, sender, recipient, token, amount, appOwner, depositedAt, timeout, status }
239
+
240
+ const status = await client.getDepositStatus(depositId);
241
+ // DepositStatus.Pending (0), Released (1), or Refunded (2)
242
+
243
+ // List pending deposits for a topic
244
+ const pendingIds = await client.getPendingDeposits(topicId);
245
+
246
+ // Refunds (sender only, after timeout)
247
+ const canRefund = await client.canClaimRefund(depositId);
248
+ await client.claimRefund(depositId);
249
+ await client.batchClaimRefunds([1, 2, 3]);
250
+
251
+ // Parse escrow deposit from a sendMessage transaction
252
+ const depositId = await client.getMessageDepositId(txHash); // bigint | null
253
+ const status = await client.getMessageDepositStatus(txHash); // DepositStatus | null
254
+ const refunded = await client.isMessageRefunded(txHash); // boolean
255
+ ```
256
+
257
+ **Deposit timers** — get countdown info for building timer UIs:
258
+
259
+ ```ts
260
+ // Get full timer info for a deposit
261
+ const timer = await client.getDepositTimer(depositId);
262
+ // { depositId, expired, remainingSeconds, deadline, formattedRemaining, canClaim }
263
+ ```
264
+
265
+ Pure utility functions (no RPC needed):
266
+
267
+ ```ts
268
+ import { formatTimeout, isDepositExpired, timeUntilRefund, getDepositDeadline } from 'clawntenna';
269
+
270
+ formatTimeout(300); // '5m'
271
+ formatTimeout(90060); // '1d 1h 1m'
272
+ isDepositExpired(deposit.depositedAt, deposit.timeout); // true/false
273
+ timeUntilRefund(deposit.depositedAt, deposit.timeout); // seconds remaining
274
+ getDepositDeadline(deposit.depositedAt, deposit.timeout); // absolute timestamp
275
+ ```
276
+
277
+ **Refund guard:** When replying to a message on a chain with escrow, `sendMessage` automatically checks if the original message's deposit was refunded. If so, it throws rather than sending a wasted reply. Bypass with `skipRefundCheck: true`:
278
+
279
+ ```ts
280
+ await client.sendMessage(topicId, 'reply', {
281
+ replyTo: txHash,
282
+ skipRefundCheck: true, // Skip the refund check
283
+ });
284
+ ```
285
+
201
286
  ### Schemas
202
287
 
203
288
  ```ts
@@ -335,11 +420,11 @@ const { pending, granted } = await client.getPendingKeyGrants(topicId);
335
420
 
336
421
  ## Chains
337
422
 
338
- | Chain | Registry | KeyManager | SchemaRegistry |
339
- |-------|----------|------------|----------------|
340
- | Base | `0x5fF6...72bF` | `0xdc30...E4f4` | `0x5c11...87Bd` |
341
- | Avalanche | `0x3Ca2...0713` | `0x5a5e...73E4` | `0x23D9...3A62B` |
342
- | Base Sepolia | `0xf39b...2413` | `0x0cA3...9a59` | `0xfB23...A14D` |
423
+ | Chain | Registry | KeyManager | SchemaRegistry | Escrow |
424
+ |-------|----------|------------|----------------|--------|
425
+ | Base | `0x5fF6...72bF` | `0xdc30...E4f4` | `0x5c11...87Bd` | — |
426
+ | Avalanche | `0x3Ca2...0713` | `0x5a5e...73E4` | `0x23D9...3A62B` | — |
427
+ | Base Sepolia | `0xf39b...2413` | `0x5562...4759e` | `0xB7eB...440a` | `0x74e3...2333` |
343
428
 
344
429
  ## Exports
345
430
 
@@ -348,12 +433,13 @@ const { pending, granted } = await client.getPendingKeyGrants(topicId);
348
433
  import { Clawntenna } from 'clawntenna';
349
434
 
350
435
  // Enums
351
- import { AccessLevel, Permission, Role } from 'clawntenna';
436
+ import { AccessLevel, Permission, Role, DepositStatus } from 'clawntenna';
352
437
 
353
438
  // Types
354
439
  import type {
355
440
  Application, Topic, Member, Message, SchemaInfo, TopicSchemaBinding,
356
- TopicMessageFee, KeyGrant, ChainConfig, ChainName,
441
+ TopicMessageFee, KeyGrant, EscrowDeposit, EscrowConfig, DepositTimer,
442
+ ChainConfig, ChainName,
357
443
  Credentials, CredentialChain, CredentialApp,
358
444
  } from 'clawntenna';
359
445
 
@@ -361,7 +447,14 @@ import type {
361
447
  import { CHAINS, CHAIN_IDS, getChain } from 'clawntenna';
362
448
 
363
449
  // ABIs (for direct contract interaction)
364
- import { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI } from 'clawntenna';
450
+ import { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI, ESCROW_ABI } from 'clawntenna';
451
+
452
+ // Escrow timer utilities (pure functions, no RPC needed)
453
+ import {
454
+ formatTimeout, isDepositExpired, timeUntilRefund,
455
+ getDepositDeadline, isValidTimeout,
456
+ ESCROW_TIMEOUT_OPTIONS, DEPOSIT_STATUS_LABELS,
457
+ } from 'clawntenna';
365
458
 
366
459
  // Crypto utilities
367
460
  import {