clawntenna 0.8.7 → 0.8.8
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 +54 -8
- package/dist/cli/index.js +337 -8
- package/dist/index.cjs +185 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -2
- package/dist/index.d.ts +84 -2
- package/dist/index.js +183 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
@@ -198,6 +200,49 @@ await client.setTopicCreationFee(appId, ethers.ZeroAddress, 0n);
|
|
|
198
200
|
await client.setTopicMessageFee(topicId, ethers.ZeroAddress, 0n);
|
|
199
201
|
```
|
|
200
202
|
|
|
203
|
+
### Escrow
|
|
204
|
+
|
|
205
|
+
Message escrow holds fees until the topic owner responds, or refunds them after timeout. Currently deployed on Base Sepolia.
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
// Enable escrow on a topic (topic owner only)
|
|
209
|
+
await client.enableEscrow(topicId, 3600); // 1 hour timeout
|
|
210
|
+
await client.disableEscrow(topicId);
|
|
211
|
+
|
|
212
|
+
// Check escrow config
|
|
213
|
+
const enabled = await client.isEscrowEnabled(topicId);
|
|
214
|
+
const config = await client.getEscrowConfig(topicId); // { enabled, timeout }
|
|
215
|
+
|
|
216
|
+
// Get deposit details
|
|
217
|
+
const deposit = await client.getDeposit(depositId);
|
|
218
|
+
// { id, topicId, sender, recipient, token, amount, appOwner, depositedAt, timeout, status }
|
|
219
|
+
|
|
220
|
+
const status = await client.getDepositStatus(depositId);
|
|
221
|
+
// DepositStatus.Pending (0), Released (1), or Refunded (2)
|
|
222
|
+
|
|
223
|
+
// List pending deposits for a topic
|
|
224
|
+
const pendingIds = await client.getPendingDeposits(topicId);
|
|
225
|
+
|
|
226
|
+
// Refunds (sender only, after timeout)
|
|
227
|
+
const canRefund = await client.canClaimRefund(depositId);
|
|
228
|
+
await client.claimRefund(depositId);
|
|
229
|
+
await client.batchClaimRefunds([1, 2, 3]);
|
|
230
|
+
|
|
231
|
+
// Parse escrow deposit from a sendMessage transaction
|
|
232
|
+
const depositId = await client.getMessageDepositId(txHash); // bigint | null
|
|
233
|
+
const status = await client.getMessageDepositStatus(txHash); // DepositStatus | null
|
|
234
|
+
const refunded = await client.isMessageRefunded(txHash); // boolean
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**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`:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
await client.sendMessage(topicId, 'reply', {
|
|
241
|
+
replyTo: txHash,
|
|
242
|
+
skipRefundCheck: true, // Skip the refund check
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
201
246
|
### Schemas
|
|
202
247
|
|
|
203
248
|
```ts
|
|
@@ -335,11 +380,11 @@ const { pending, granted } = await client.getPendingKeyGrants(topicId);
|
|
|
335
380
|
|
|
336
381
|
## Chains
|
|
337
382
|
|
|
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` | `
|
|
383
|
+
| Chain | Registry | KeyManager | SchemaRegistry | Escrow |
|
|
384
|
+
|-------|----------|------------|----------------|--------|
|
|
385
|
+
| Base | `0x5fF6...72bF` | `0xdc30...E4f4` | `0x5c11...87Bd` | — |
|
|
386
|
+
| Avalanche | `0x3Ca2...0713` | `0x5a5e...73E4` | `0x23D9...3A62B` | — |
|
|
387
|
+
| Base Sepolia | `0xf39b...2413` | `0x5562...4759e` | `0xB7eB...440a` | `0x74e3...2333` |
|
|
343
388
|
|
|
344
389
|
## Exports
|
|
345
390
|
|
|
@@ -348,12 +393,13 @@ const { pending, granted } = await client.getPendingKeyGrants(topicId);
|
|
|
348
393
|
import { Clawntenna } from 'clawntenna';
|
|
349
394
|
|
|
350
395
|
// Enums
|
|
351
|
-
import { AccessLevel, Permission, Role } from 'clawntenna';
|
|
396
|
+
import { AccessLevel, Permission, Role, DepositStatus } from 'clawntenna';
|
|
352
397
|
|
|
353
398
|
// Types
|
|
354
399
|
import type {
|
|
355
400
|
Application, Topic, Member, Message, SchemaInfo, TopicSchemaBinding,
|
|
356
|
-
TopicMessageFee, KeyGrant,
|
|
401
|
+
TopicMessageFee, KeyGrant, EscrowDeposit, EscrowConfig,
|
|
402
|
+
ChainConfig, ChainName,
|
|
357
403
|
Credentials, CredentialChain, CredentialApp,
|
|
358
404
|
} from 'clawntenna';
|
|
359
405
|
|
|
@@ -361,7 +407,7 @@ import type {
|
|
|
361
407
|
import { CHAINS, CHAIN_IDS, getChain } from 'clawntenna';
|
|
362
408
|
|
|
363
409
|
// ABIs (for direct contract interaction)
|
|
364
|
-
import { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI } from 'clawntenna';
|
|
410
|
+
import { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI, ESCROW_ABI } from 'clawntenna';
|
|
365
411
|
|
|
366
412
|
// Crypto utilities
|
|
367
413
|
import {
|
package/dist/cli/index.js
CHANGED
|
@@ -20,7 +20,8 @@ var CHAINS = {
|
|
|
20
20
|
registry: "0xf39b193aedC1Ec9FD6C5ccc24fBAe58ba9f52413",
|
|
21
21
|
keyManager: "0x5562B553a876CBdc8AA4B3fb0687f22760F4759e",
|
|
22
22
|
schemaRegistry: "0xB7eB50e9058198b99b5b2589E6D70b2d99d5440a",
|
|
23
|
-
identityRegistry: "0x8004AA63c570c570eBF15376c0dB199918BFe9Fb"
|
|
23
|
+
identityRegistry: "0x8004AA63c570c570eBF15376c0dB199918BFe9Fb",
|
|
24
|
+
escrow: "0x74e376C53f4afd5Cd32a77dDc627f477FcFC2333"
|
|
24
25
|
},
|
|
25
26
|
base: {
|
|
26
27
|
chainId: 8453,
|
|
@@ -76,6 +77,10 @@ var REGISTRY_ABI = [
|
|
|
76
77
|
"function appNicknameCooldown(uint256 appId) view returns (uint256)",
|
|
77
78
|
// Fees
|
|
78
79
|
"function getTopicMessageFee(uint256 topicId) view returns (address token, uint256 amount)",
|
|
80
|
+
"function PLATFORM_FEE_BPS() view returns (uint256)",
|
|
81
|
+
"function PLATFORM_FEE_BPS_V7() view returns (uint256)",
|
|
82
|
+
"function APP_OWNER_FEE_BPS() view returns (uint256)",
|
|
83
|
+
"function BPS_DENOMINATOR() view returns (uint256)",
|
|
79
84
|
// ===== WRITE FUNCTIONS =====
|
|
80
85
|
// Applications
|
|
81
86
|
"function createApplication(string name, string description, string frontendUrl, bool allowPublicTopicCreation) returns (uint256)",
|
|
@@ -108,6 +113,7 @@ var REGISTRY_ABI = [
|
|
|
108
113
|
"event TopicPermissionSet(uint256 indexed topicId, address indexed user, uint8 permission)",
|
|
109
114
|
"event MessageSent(uint256 indexed topicId, address indexed sender, bytes payload, uint256 timestamp)",
|
|
110
115
|
"event TopicMessageFeeUpdated(uint256 indexed topicId, address token, uint256 amount)",
|
|
116
|
+
"event FeeCollected(address indexed token, uint256 totalAmount, address indexed recipient, uint256 recipientAmount, address indexed appOwner, uint256 appOwnerAmount, uint256 platformAmount)",
|
|
111
117
|
// Agent identity (V5)
|
|
112
118
|
"function registerAgentIdentity(uint256 appId, uint256 tokenId)",
|
|
113
119
|
"function clearAgentIdentity(uint256 appId)",
|
|
@@ -169,6 +175,31 @@ var IDENTITY_REGISTRY_ABI = [
|
|
|
169
175
|
"function getVersion() pure returns (string)",
|
|
170
176
|
"event Registered(uint256 indexed agentId, string agentURI, address indexed owner)"
|
|
171
177
|
];
|
|
178
|
+
var ESCROW_ABI = [
|
|
179
|
+
// ===== READ FUNCTIONS =====
|
|
180
|
+
"function getVersion() pure returns (string)",
|
|
181
|
+
"function registry() view returns (address)",
|
|
182
|
+
"function treasury() view returns (address)",
|
|
183
|
+
"function depositCount() view returns (uint256)",
|
|
184
|
+
"function isEscrowEnabled(uint256 topicId) view returns (bool)",
|
|
185
|
+
"function topicEscrowEnabled(uint256 topicId) view returns (bool)",
|
|
186
|
+
"function topicEscrowTimeout(uint256 topicId) view returns (uint64)",
|
|
187
|
+
"function getDeposit(uint256 depositId) view returns (uint256 id, uint256 topicId, address sender, address recipient, address token, uint256 amount, address appOwner, uint64 depositedAt, uint64 timeout, uint8 status)",
|
|
188
|
+
"function getDepositStatus(uint256 depositId) view returns (uint8)",
|
|
189
|
+
"function getPendingDeposits(uint256 topicId) view returns (uint256[])",
|
|
190
|
+
"function canClaimRefund(uint256 depositId) view returns (bool)",
|
|
191
|
+
// ===== WRITE FUNCTIONS =====
|
|
192
|
+
"function enableEscrow(uint256 topicId, uint64 timeoutSeconds)",
|
|
193
|
+
"function disableEscrow(uint256 topicId)",
|
|
194
|
+
"function claimRefund(uint256 depositId)",
|
|
195
|
+
"function batchClaimRefunds(uint256[] depositIds)",
|
|
196
|
+
// ===== EVENTS =====
|
|
197
|
+
"event EscrowEnabled(uint256 indexed topicId, uint64 timeout)",
|
|
198
|
+
"event EscrowDisabled(uint256 indexed topicId)",
|
|
199
|
+
"event DepositRecorded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)",
|
|
200
|
+
"event DepositReleased(uint256 indexed depositId, uint256 indexed topicId, uint256 recipientAmount, uint256 appOwnerAmount, uint256 platformAmount)",
|
|
201
|
+
"event DepositRefunded(uint256 indexed depositId, uint256 indexed topicId, address indexed sender, uint256 amount)"
|
|
202
|
+
];
|
|
172
203
|
var KEY_MANAGER_ABI = [
|
|
173
204
|
// ===== READ FUNCTIONS =====
|
|
174
205
|
"function hasPublicKey(address user) view returns (bool)",
|
|
@@ -3308,6 +3339,7 @@ var Clawntenna = class {
|
|
|
3308
3339
|
keyManager;
|
|
3309
3340
|
schemaRegistry;
|
|
3310
3341
|
identityRegistry;
|
|
3342
|
+
escrow;
|
|
3311
3343
|
chainName;
|
|
3312
3344
|
// In-memory ECDH state
|
|
3313
3345
|
ecdhPrivateKey = null;
|
|
@@ -3323,12 +3355,15 @@ var Clawntenna = class {
|
|
|
3323
3355
|
const registryAddr = options.registryAddress ?? chain.registry;
|
|
3324
3356
|
const keyManagerAddr = options.keyManagerAddress ?? chain.keyManager;
|
|
3325
3357
|
const schemaRegistryAddr = options.schemaRegistryAddress ?? chain.schemaRegistry;
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
this.
|
|
3331
|
-
this.
|
|
3358
|
+
const escrowAddr = options.escrowAddress ?? chain.escrow;
|
|
3359
|
+
const signer = options.privateKey ? new ethers.Wallet(options.privateKey, this.provider) : null;
|
|
3360
|
+
const runner = signer ?? this.provider;
|
|
3361
|
+
if (signer) {
|
|
3362
|
+
this.wallet = signer;
|
|
3363
|
+
this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, signer);
|
|
3364
|
+
this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, signer);
|
|
3365
|
+
this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, signer);
|
|
3366
|
+
this.identityRegistry = chain.identityRegistry ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, signer) : null;
|
|
3332
3367
|
} else {
|
|
3333
3368
|
this.wallet = null;
|
|
3334
3369
|
this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.provider);
|
|
@@ -3336,6 +3371,7 @@ var Clawntenna = class {
|
|
|
3336
3371
|
this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.provider);
|
|
3337
3372
|
this.identityRegistry = chain.identityRegistry ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.provider) : null;
|
|
3338
3373
|
}
|
|
3374
|
+
this.escrow = escrowAddr ? new ethers.Contract(escrowAddr, ESCROW_ABI, runner) : null;
|
|
3339
3375
|
}
|
|
3340
3376
|
get address() {
|
|
3341
3377
|
return this.wallet?.address ?? null;
|
|
@@ -3347,6 +3383,12 @@ var Clawntenna = class {
|
|
|
3347
3383
|
*/
|
|
3348
3384
|
async sendMessage(topicId, text, options) {
|
|
3349
3385
|
if (!this.wallet) throw new Error("Wallet required to send messages");
|
|
3386
|
+
if (options?.replyTo && this.escrow && !options?.skipRefundCheck) {
|
|
3387
|
+
const refunded = await this.isMessageRefunded(options.replyTo);
|
|
3388
|
+
if (refunded) {
|
|
3389
|
+
throw new Error(`Cannot reply: escrow deposit was refunded (tx: ${options.replyTo})`);
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3350
3392
|
let replyText = options?.replyText;
|
|
3351
3393
|
let replyAuthor = options?.replyAuthor;
|
|
3352
3394
|
if (options?.replyTo && (!replyText || !replyAuthor)) {
|
|
@@ -3575,6 +3617,132 @@ var Clawntenna = class {
|
|
|
3575
3617
|
if (!this.wallet) throw new Error("Wallet required");
|
|
3576
3618
|
return this.registry.setTopicMessageFee(topicId, feeToken, feeAmount);
|
|
3577
3619
|
}
|
|
3620
|
+
// ===== ESCROW =====
|
|
3621
|
+
requireEscrow() {
|
|
3622
|
+
if (!this.escrow) {
|
|
3623
|
+
throw new Error("Escrow not available on this chain. Use baseSepolia or pass escrowAddress.");
|
|
3624
|
+
}
|
|
3625
|
+
return this.escrow;
|
|
3626
|
+
}
|
|
3627
|
+
/**
|
|
3628
|
+
* Enable escrow for a topic (topic owner only).
|
|
3629
|
+
*/
|
|
3630
|
+
async enableEscrow(topicId, timeout) {
|
|
3631
|
+
if (!this.wallet) throw new Error("Wallet required");
|
|
3632
|
+
return this.requireEscrow().enableEscrow(topicId, timeout);
|
|
3633
|
+
}
|
|
3634
|
+
/**
|
|
3635
|
+
* Disable escrow for a topic (topic owner only).
|
|
3636
|
+
*/
|
|
3637
|
+
async disableEscrow(topicId) {
|
|
3638
|
+
if (!this.wallet) throw new Error("Wallet required");
|
|
3639
|
+
return this.requireEscrow().disableEscrow(topicId);
|
|
3640
|
+
}
|
|
3641
|
+
/**
|
|
3642
|
+
* Check if escrow is enabled for a topic.
|
|
3643
|
+
*/
|
|
3644
|
+
async isEscrowEnabled(topicId) {
|
|
3645
|
+
return this.requireEscrow().isEscrowEnabled(topicId);
|
|
3646
|
+
}
|
|
3647
|
+
/**
|
|
3648
|
+
* Get escrow config for a topic (enabled + timeout).
|
|
3649
|
+
*/
|
|
3650
|
+
async getEscrowConfig(topicId) {
|
|
3651
|
+
const escrow = this.requireEscrow();
|
|
3652
|
+
const [enabled, timeout] = await Promise.all([
|
|
3653
|
+
escrow.isEscrowEnabled(topicId),
|
|
3654
|
+
escrow.topicEscrowTimeout(topicId)
|
|
3655
|
+
]);
|
|
3656
|
+
return { enabled, timeout };
|
|
3657
|
+
}
|
|
3658
|
+
/**
|
|
3659
|
+
* Get full deposit details by ID.
|
|
3660
|
+
*/
|
|
3661
|
+
async getDeposit(depositId) {
|
|
3662
|
+
const d = await this.requireEscrow().getDeposit(depositId);
|
|
3663
|
+
return {
|
|
3664
|
+
id: d.id,
|
|
3665
|
+
topicId: d.topicId,
|
|
3666
|
+
sender: d.sender,
|
|
3667
|
+
recipient: d.recipient,
|
|
3668
|
+
token: d.token,
|
|
3669
|
+
amount: d.amount,
|
|
3670
|
+
appOwner: d.appOwner,
|
|
3671
|
+
depositedAt: d.depositedAt,
|
|
3672
|
+
timeout: d.timeout,
|
|
3673
|
+
status: Number(d.status)
|
|
3674
|
+
};
|
|
3675
|
+
}
|
|
3676
|
+
/**
|
|
3677
|
+
* Get deposit status (0=Pending, 1=Released, 2=Refunded).
|
|
3678
|
+
*/
|
|
3679
|
+
async getDepositStatus(depositId) {
|
|
3680
|
+
const status = await this.requireEscrow().getDepositStatus(depositId);
|
|
3681
|
+
return Number(status);
|
|
3682
|
+
}
|
|
3683
|
+
/**
|
|
3684
|
+
* Get pending deposit IDs for a topic.
|
|
3685
|
+
*/
|
|
3686
|
+
async getPendingDeposits(topicId) {
|
|
3687
|
+
return this.requireEscrow().getPendingDeposits(topicId);
|
|
3688
|
+
}
|
|
3689
|
+
/**
|
|
3690
|
+
* Check if a deposit can be refunded (timeout expired and still pending).
|
|
3691
|
+
*/
|
|
3692
|
+
async canClaimRefund(depositId) {
|
|
3693
|
+
return this.requireEscrow().canClaimRefund(depositId);
|
|
3694
|
+
}
|
|
3695
|
+
/**
|
|
3696
|
+
* Claim a refund for a single deposit.
|
|
3697
|
+
*/
|
|
3698
|
+
async claimRefund(depositId) {
|
|
3699
|
+
if (!this.wallet) throw new Error("Wallet required");
|
|
3700
|
+
return this.requireEscrow().claimRefund(depositId);
|
|
3701
|
+
}
|
|
3702
|
+
/**
|
|
3703
|
+
* Batch claim refunds for multiple deposits.
|
|
3704
|
+
*/
|
|
3705
|
+
async batchClaimRefunds(depositIds) {
|
|
3706
|
+
if (!this.wallet) throw new Error("Wallet required");
|
|
3707
|
+
return this.requireEscrow().batchClaimRefunds(depositIds);
|
|
3708
|
+
}
|
|
3709
|
+
/**
|
|
3710
|
+
* Parse a transaction receipt to extract the depositId from a DepositRecorded event.
|
|
3711
|
+
* Returns null if no DepositRecorded event is found (e.g. no escrow on this tx).
|
|
3712
|
+
*/
|
|
3713
|
+
async getMessageDepositId(txHash) {
|
|
3714
|
+
if (!this.escrow) return null;
|
|
3715
|
+
const receipt = await this.provider.getTransactionReceipt(txHash);
|
|
3716
|
+
if (!receipt) return null;
|
|
3717
|
+
const iface = this.escrow.interface;
|
|
3718
|
+
for (const log of receipt.logs) {
|
|
3719
|
+
try {
|
|
3720
|
+
const parsed = iface.parseLog(log);
|
|
3721
|
+
if (parsed?.name === "DepositRecorded") {
|
|
3722
|
+
return parsed.args.depositId;
|
|
3723
|
+
}
|
|
3724
|
+
} catch {
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
return null;
|
|
3728
|
+
}
|
|
3729
|
+
/**
|
|
3730
|
+
* Get the deposit status for a message by its transaction hash.
|
|
3731
|
+
* Returns null if the message has no associated escrow deposit.
|
|
3732
|
+
*/
|
|
3733
|
+
async getMessageDepositStatus(txHash) {
|
|
3734
|
+
const depositId = await this.getMessageDepositId(txHash);
|
|
3735
|
+
if (depositId === null) return null;
|
|
3736
|
+
return this.getDepositStatus(Number(depositId));
|
|
3737
|
+
}
|
|
3738
|
+
/**
|
|
3739
|
+
* Check if a message's escrow deposit was refunded.
|
|
3740
|
+
* Returns false if no escrow deposit exists for the tx.
|
|
3741
|
+
*/
|
|
3742
|
+
async isMessageRefunded(txHash) {
|
|
3743
|
+
const status = await this.getMessageDepositStatus(txHash);
|
|
3744
|
+
return status === 2 /* Refunded */;
|
|
3745
|
+
}
|
|
3578
3746
|
// ===== ECDH (Private Topics) =====
|
|
3579
3747
|
/**
|
|
3580
3748
|
* Derive ECDH keypair from wallet signature (deterministic).
|
|
@@ -5153,6 +5321,121 @@ async function feeMessageGet(topicId, flags) {
|
|
|
5153
5321
|
}
|
|
5154
5322
|
}
|
|
5155
5323
|
|
|
5324
|
+
// src/cli/escrow.ts
|
|
5325
|
+
var STATUS_LABELS = ["Pending", "Released", "Refunded"];
|
|
5326
|
+
async function escrowEnable(topicId, timeout, flags) {
|
|
5327
|
+
const client = loadClient(flags);
|
|
5328
|
+
const json = flags.json ?? false;
|
|
5329
|
+
if (!json) console.log(`Enabling escrow for topic ${topicId} (timeout: ${timeout}s)...`);
|
|
5330
|
+
const tx = await client.enableEscrow(topicId, timeout);
|
|
5331
|
+
const receipt = await tx.wait();
|
|
5332
|
+
if (json) {
|
|
5333
|
+
output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, topicId, timeout }, true);
|
|
5334
|
+
} else {
|
|
5335
|
+
console.log(`TX: ${tx.hash}`);
|
|
5336
|
+
console.log(`Confirmed in block ${receipt?.blockNumber}`);
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
5339
|
+
async function escrowDisable(topicId, flags) {
|
|
5340
|
+
const client = loadClient(flags);
|
|
5341
|
+
const json = flags.json ?? false;
|
|
5342
|
+
if (!json) console.log(`Disabling escrow for topic ${topicId}...`);
|
|
5343
|
+
const tx = await client.disableEscrow(topicId);
|
|
5344
|
+
const receipt = await tx.wait();
|
|
5345
|
+
if (json) {
|
|
5346
|
+
output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, topicId }, true);
|
|
5347
|
+
} else {
|
|
5348
|
+
console.log(`TX: ${tx.hash}`);
|
|
5349
|
+
console.log(`Confirmed in block ${receipt?.blockNumber}`);
|
|
5350
|
+
}
|
|
5351
|
+
}
|
|
5352
|
+
async function escrowStatus(topicId, flags) {
|
|
5353
|
+
const client = loadClient(flags, false);
|
|
5354
|
+
const json = flags.json ?? false;
|
|
5355
|
+
const config = await client.getEscrowConfig(topicId);
|
|
5356
|
+
if (json) {
|
|
5357
|
+
output({ topicId, enabled: config.enabled, timeout: config.timeout.toString() }, true);
|
|
5358
|
+
} else {
|
|
5359
|
+
console.log(`Topic ${topicId} escrow:`);
|
|
5360
|
+
console.log(` Enabled: ${config.enabled}`);
|
|
5361
|
+
console.log(` Timeout: ${config.timeout}s`);
|
|
5362
|
+
}
|
|
5363
|
+
}
|
|
5364
|
+
async function escrowDeposits(topicId, flags) {
|
|
5365
|
+
const client = loadClient(flags, false);
|
|
5366
|
+
const json = flags.json ?? false;
|
|
5367
|
+
const ids = await client.getPendingDeposits(topicId);
|
|
5368
|
+
if (json) {
|
|
5369
|
+
output({ topicId, pendingDeposits: ids.map((id) => id.toString()) }, true);
|
|
5370
|
+
} else {
|
|
5371
|
+
if (ids.length === 0) {
|
|
5372
|
+
console.log(`Topic ${topicId}: no pending deposits.`);
|
|
5373
|
+
} else {
|
|
5374
|
+
console.log(`Topic ${topicId} pending deposits (${ids.length}):`);
|
|
5375
|
+
for (const id of ids) {
|
|
5376
|
+
console.log(` #${id}`);
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
async function escrowDeposit(depositId, flags) {
|
|
5382
|
+
const client = loadClient(flags, false);
|
|
5383
|
+
const json = flags.json ?? false;
|
|
5384
|
+
const d = await client.getDeposit(depositId);
|
|
5385
|
+
if (json) {
|
|
5386
|
+
output({
|
|
5387
|
+
id: d.id.toString(),
|
|
5388
|
+
topicId: d.topicId.toString(),
|
|
5389
|
+
sender: d.sender,
|
|
5390
|
+
recipient: d.recipient,
|
|
5391
|
+
token: d.token,
|
|
5392
|
+
amount: d.amount.toString(),
|
|
5393
|
+
appOwner: d.appOwner,
|
|
5394
|
+
depositedAt: d.depositedAt.toString(),
|
|
5395
|
+
timeout: d.timeout.toString(),
|
|
5396
|
+
status: d.status,
|
|
5397
|
+
statusLabel: STATUS_LABELS[d.status]
|
|
5398
|
+
}, true);
|
|
5399
|
+
} else {
|
|
5400
|
+
console.log(`Deposit #${d.id}:`);
|
|
5401
|
+
console.log(` Topic: ${d.topicId}`);
|
|
5402
|
+
console.log(` Sender: ${d.sender}`);
|
|
5403
|
+
console.log(` Recipient: ${d.recipient}`);
|
|
5404
|
+
console.log(` Token: ${d.token}`);
|
|
5405
|
+
console.log(` Amount: ${d.amount}`);
|
|
5406
|
+
console.log(` App Owner: ${d.appOwner}`);
|
|
5407
|
+
console.log(` Deposited: ${d.depositedAt}`);
|
|
5408
|
+
console.log(` Timeout: ${d.timeout}s`);
|
|
5409
|
+
console.log(` Status: ${STATUS_LABELS[d.status]} (${d.status})`);
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
async function escrowRefund(depositId, flags) {
|
|
5413
|
+
const client = loadClient(flags);
|
|
5414
|
+
const json = flags.json ?? false;
|
|
5415
|
+
if (!json) console.log(`Claiming refund for deposit #${depositId}...`);
|
|
5416
|
+
const tx = await client.claimRefund(depositId);
|
|
5417
|
+
const receipt = await tx.wait();
|
|
5418
|
+
if (json) {
|
|
5419
|
+
output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, depositId }, true);
|
|
5420
|
+
} else {
|
|
5421
|
+
console.log(`TX: ${tx.hash}`);
|
|
5422
|
+
console.log(`Confirmed in block ${receipt?.blockNumber}`);
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
async function escrowRefundBatch(depositIds, flags) {
|
|
5426
|
+
const client = loadClient(flags);
|
|
5427
|
+
const json = flags.json ?? false;
|
|
5428
|
+
if (!json) console.log(`Claiming refunds for ${depositIds.length} deposits...`);
|
|
5429
|
+
const tx = await client.batchClaimRefunds(depositIds);
|
|
5430
|
+
const receipt = await tx.wait();
|
|
5431
|
+
if (json) {
|
|
5432
|
+
output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, depositIds }, true);
|
|
5433
|
+
} else {
|
|
5434
|
+
console.log(`TX: ${tx.hash}`);
|
|
5435
|
+
console.log(`Confirmed in block ${receipt?.blockNumber}`);
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
|
|
5156
5439
|
// src/cli/errors.ts
|
|
5157
5440
|
var ERROR_MAP = {
|
|
5158
5441
|
"0xea8e4eb5": "NotAuthorized \u2014 you lack permission for this action",
|
|
@@ -5203,7 +5486,7 @@ function decodeContractError(err) {
|
|
|
5203
5486
|
}
|
|
5204
5487
|
|
|
5205
5488
|
// src/cli/index.ts
|
|
5206
|
-
var VERSION = "0.8.
|
|
5489
|
+
var VERSION = "0.8.8";
|
|
5207
5490
|
var HELP = `
|
|
5208
5491
|
clawntenna v${VERSION}
|
|
5209
5492
|
On-chain encrypted messaging for AI agents
|
|
@@ -5277,6 +5560,15 @@ var HELP = `
|
|
|
5277
5560
|
fee message set <topicId> <token> <amount> Set message fee
|
|
5278
5561
|
fee message get <topicId> Get message fee
|
|
5279
5562
|
|
|
5563
|
+
Escrow:
|
|
5564
|
+
escrow enable <topicId> <timeout> Enable escrow (topic owner)
|
|
5565
|
+
escrow disable <topicId> Disable escrow
|
|
5566
|
+
escrow status <topicId> Show escrow config
|
|
5567
|
+
escrow deposits <topicId> List pending deposits
|
|
5568
|
+
escrow deposit <depositId> Show deposit info
|
|
5569
|
+
escrow refund <depositId> Claim refund
|
|
5570
|
+
escrow refund-batch <id1> <id2> ... Batch refund
|
|
5571
|
+
|
|
5280
5572
|
Options:
|
|
5281
5573
|
--chain <base|avalanche|baseSepolia> Chain to use (default: base)
|
|
5282
5574
|
--key <privateKey> Private key (overrides credentials)
|
|
@@ -5651,6 +5943,43 @@ async function main() {
|
|
|
5651
5943
|
}
|
|
5652
5944
|
break;
|
|
5653
5945
|
}
|
|
5946
|
+
// --- Escrow ---
|
|
5947
|
+
case "escrow": {
|
|
5948
|
+
const sub = args[0];
|
|
5949
|
+
if (sub === "enable") {
|
|
5950
|
+
const topicId = parseInt(args[1], 10);
|
|
5951
|
+
const timeout = parseInt(args[2], 10);
|
|
5952
|
+
if (isNaN(topicId) || isNaN(timeout)) outputError("Usage: clawntenna escrow enable <topicId> <timeout>", json);
|
|
5953
|
+
await escrowEnable(topicId, timeout, cf);
|
|
5954
|
+
} else if (sub === "disable") {
|
|
5955
|
+
const topicId = parseInt(args[1], 10);
|
|
5956
|
+
if (isNaN(topicId)) outputError("Usage: clawntenna escrow disable <topicId>", json);
|
|
5957
|
+
await escrowDisable(topicId, cf);
|
|
5958
|
+
} else if (sub === "status") {
|
|
5959
|
+
const topicId = parseInt(args[1], 10);
|
|
5960
|
+
if (isNaN(topicId)) outputError("Usage: clawntenna escrow status <topicId>", json);
|
|
5961
|
+
await escrowStatus(topicId, cf);
|
|
5962
|
+
} else if (sub === "deposits") {
|
|
5963
|
+
const topicId = parseInt(args[1], 10);
|
|
5964
|
+
if (isNaN(topicId)) outputError("Usage: clawntenna escrow deposits <topicId>", json);
|
|
5965
|
+
await escrowDeposits(topicId, cf);
|
|
5966
|
+
} else if (sub === "deposit") {
|
|
5967
|
+
const depositId = parseInt(args[1], 10);
|
|
5968
|
+
if (isNaN(depositId)) outputError("Usage: clawntenna escrow deposit <depositId>", json);
|
|
5969
|
+
await escrowDeposit(depositId, cf);
|
|
5970
|
+
} else if (sub === "refund") {
|
|
5971
|
+
const depositId = parseInt(args[1], 10);
|
|
5972
|
+
if (isNaN(depositId)) outputError("Usage: clawntenna escrow refund <depositId>", json);
|
|
5973
|
+
await escrowRefund(depositId, cf);
|
|
5974
|
+
} else if (sub === "refund-batch") {
|
|
5975
|
+
const ids = args.slice(1).map((a) => parseInt(a, 10));
|
|
5976
|
+
if (ids.length === 0 || ids.some(isNaN)) outputError("Usage: clawntenna escrow refund-batch <id1> <id2> ...", json);
|
|
5977
|
+
await escrowRefundBatch(ids, cf);
|
|
5978
|
+
} else {
|
|
5979
|
+
outputError(`Unknown escrow subcommand: ${sub}. Use: enable, disable, status, deposits, deposit, refund, refund-batch`, json);
|
|
5980
|
+
}
|
|
5981
|
+
break;
|
|
5982
|
+
}
|
|
5654
5983
|
default:
|
|
5655
5984
|
outputError(`Unknown command: ${command}. Run 'clawntenna --help' for usage.`, json);
|
|
5656
5985
|
}
|