shll-skills 3.1.0 → 4.0.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.
@@ -0,0 +1,502 @@
1
+ // node_modules/shll-policy-sdk/dist/index.js
2
+ import {
3
+ createPublicClient,
4
+ createWalletClient,
5
+ http
6
+ } from "viem";
7
+ import { privateKeyToAccount } from "viem/accounts";
8
+ import { bsc, bscTestnet } from "viem/chains";
9
+ var PolicyGuardV4Abi = [
10
+ {
11
+ name: "validate",
12
+ type: "function",
13
+ stateMutability: "view",
14
+ inputs: [
15
+ { name: "nfa", type: "address" },
16
+ { name: "tokenId", type: "uint256" },
17
+ { name: "agentAccount", type: "address" },
18
+ { name: "caller", type: "address" },
19
+ {
20
+ name: "action",
21
+ type: "tuple",
22
+ components: [
23
+ { name: "target", type: "address" },
24
+ { name: "value", type: "uint256" },
25
+ { name: "data", type: "bytes" }
26
+ ]
27
+ }
28
+ ],
29
+ outputs: [
30
+ { name: "ok", type: "bool" },
31
+ { name: "reason", type: "string" }
32
+ ]
33
+ },
34
+ {
35
+ name: "getActivePolicies",
36
+ type: "function",
37
+ stateMutability: "view",
38
+ inputs: [{ name: "instanceId", type: "uint256" }],
39
+ outputs: [{ name: "", type: "address[]" }]
40
+ },
41
+ {
42
+ name: "getTemplatePolicies",
43
+ type: "function",
44
+ stateMutability: "view",
45
+ inputs: [{ name: "templateId", type: "bytes32" }],
46
+ outputs: [{ name: "", type: "address[]" }]
47
+ },
48
+ {
49
+ name: "instanceTemplateId",
50
+ type: "function",
51
+ stateMutability: "view",
52
+ inputs: [{ name: "instanceId", type: "uint256" }],
53
+ outputs: [{ name: "", type: "bytes32" }]
54
+ }
55
+ ];
56
+ var AgentNFAAbi = [
57
+ {
58
+ name: "execute",
59
+ type: "function",
60
+ stateMutability: "payable",
61
+ inputs: [
62
+ { name: "tokenId", type: "uint256" },
63
+ {
64
+ name: "action",
65
+ type: "tuple",
66
+ components: [
67
+ { name: "target", type: "address" },
68
+ { name: "value", type: "uint256" },
69
+ { name: "data", type: "bytes" }
70
+ ]
71
+ }
72
+ ],
73
+ outputs: [{ name: "result", type: "bytes" }]
74
+ },
75
+ {
76
+ name: "executeBatch",
77
+ type: "function",
78
+ stateMutability: "payable",
79
+ inputs: [
80
+ { name: "tokenId", type: "uint256" },
81
+ {
82
+ name: "actions",
83
+ type: "tuple[]",
84
+ components: [
85
+ { name: "target", type: "address" },
86
+ { name: "value", type: "uint256" },
87
+ { name: "data", type: "bytes" }
88
+ ]
89
+ }
90
+ ],
91
+ outputs: [{ name: "results", type: "bytes[]" }]
92
+ },
93
+ {
94
+ name: "accountOf",
95
+ type: "function",
96
+ stateMutability: "view",
97
+ inputs: [{ name: "tokenId", type: "uint256" }],
98
+ outputs: [{ name: "", type: "address" }]
99
+ },
100
+ {
101
+ name: "userOf",
102
+ type: "function",
103
+ stateMutability: "view",
104
+ inputs: [{ name: "tokenId", type: "uint256" }],
105
+ outputs: [{ name: "", type: "address" }]
106
+ },
107
+ {
108
+ name: "userExpires",
109
+ type: "function",
110
+ stateMutability: "view",
111
+ inputs: [{ name: "tokenId", type: "uint256" }],
112
+ outputs: [{ name: "", type: "uint256" }]
113
+ },
114
+ {
115
+ name: "operatorOf",
116
+ type: "function",
117
+ stateMutability: "view",
118
+ inputs: [{ name: "tokenId", type: "uint256" }],
119
+ outputs: [{ name: "", type: "address" }]
120
+ },
121
+ {
122
+ name: "operatorExpiresOf",
123
+ type: "function",
124
+ stateMutability: "view",
125
+ inputs: [{ name: "tokenId", type: "uint256" }],
126
+ outputs: [{ name: "", type: "uint256" }]
127
+ },
128
+ {
129
+ name: "operatorNonceOf",
130
+ type: "function",
131
+ stateMutability: "view",
132
+ inputs: [{ name: "tokenId", type: "uint256" }],
133
+ outputs: [{ name: "", type: "uint256" }]
134
+ },
135
+ {
136
+ name: "setOperatorWithSig",
137
+ type: "function",
138
+ stateMutability: "nonpayable",
139
+ inputs: [
140
+ {
141
+ name: "req",
142
+ type: "tuple",
143
+ components: [
144
+ { name: "tokenId", type: "uint256" },
145
+ { name: "renter", type: "address" },
146
+ { name: "operator", type: "address" },
147
+ { name: "expires", type: "uint64" },
148
+ { name: "nonce", type: "uint256" },
149
+ { name: "deadline", type: "uint256" }
150
+ ]
151
+ },
152
+ { name: "signature", type: "bytes" }
153
+ ],
154
+ outputs: []
155
+ },
156
+ {
157
+ name: "clearOperator",
158
+ type: "function",
159
+ stateMutability: "nonpayable",
160
+ inputs: [{ name: "tokenId", type: "uint256" }],
161
+ outputs: []
162
+ }
163
+ ];
164
+ var IPolicyAbi = [
165
+ {
166
+ name: "policyType",
167
+ type: "function",
168
+ stateMutability: "pure",
169
+ inputs: [],
170
+ outputs: [{ name: "", type: "bytes32" }]
171
+ },
172
+ {
173
+ name: "renterConfigurable",
174
+ type: "function",
175
+ stateMutability: "pure",
176
+ inputs: [],
177
+ outputs: [{ name: "", type: "bool" }]
178
+ },
179
+ {
180
+ name: "check",
181
+ type: "function",
182
+ stateMutability: "view",
183
+ inputs: [
184
+ { name: "instanceId", type: "uint256" },
185
+ { name: "caller", type: "address" },
186
+ { name: "target", type: "address" },
187
+ { name: "selector", type: "bytes4" },
188
+ { name: "callData", type: "bytes" },
189
+ { name: "value", type: "uint256" }
190
+ ],
191
+ outputs: [
192
+ { name: "ok", type: "bool" },
193
+ { name: "reason", type: "string" }
194
+ ]
195
+ }
196
+ ];
197
+ var POLICY_TYPE_NAMES = {
198
+ // keccak256(abi.encodePacked("spending_limit"))
199
+ "0xfc881f20d88f7ec7e942f0b796f40ce121974015a83f091cd5ef2cf146443d4e": "spending_limit",
200
+ // keccak256(abi.encodePacked("cooldown"))
201
+ "0x7230b2ae91f8efdf9ea182e9a9d1fef92adfda3e61d5512778c2f81580b13d10": "cooldown",
202
+ // keccak256(abi.encodePacked("receiver_guard"))
203
+ "0xccead04e892f4bd05d3e2491948809479bf4641d233bf37f3fac8411960a97de": "receiver_guard",
204
+ // keccak256(abi.encodePacked("dex_whitelist"))
205
+ "0xb5ba0ef255153fe2c4c7201fbd5f2658f86e25a723f1844d5bf64a5c48e0c9ed": "dex_whitelist",
206
+ // keccak256(abi.encodePacked("defi_guard"))
207
+ "0xe109b0d2bf06e570d9aecb2cd18dafe849a5c1d8b9b9f6ecd8b9ed727f816bb9": "defi_guard",
208
+ // keccak256(abi.encodePacked("token_whitelist"))
209
+ "0xf4717a1c69e76a67584dd6d514cc591ec961b52113ec085dfe157bd9e238e1f3": "token_whitelist"
210
+ };
211
+ var PolicyClient = class {
212
+ publicClient;
213
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
214
+ _walletClient;
215
+ _operatorAddress;
216
+ agentNfaAddress;
217
+ policyGuardAddress;
218
+ constructor(config) {
219
+ const chain = config.chainId === 97 ? bscTestnet : bsc;
220
+ this.publicClient = createPublicClient({
221
+ chain,
222
+ transport: http(config.rpcUrl)
223
+ });
224
+ this.agentNfaAddress = config.agentNfaAddress;
225
+ this.policyGuardAddress = config.policyGuardAddress;
226
+ if (config.operatorPrivateKey) {
227
+ const account = privateKeyToAccount(config.operatorPrivateKey);
228
+ this._operatorAddress = account.address;
229
+ this._walletClient = createWalletClient({
230
+ account,
231
+ chain,
232
+ transport: http(config.rpcUrl)
233
+ });
234
+ } else {
235
+ this._operatorAddress = null;
236
+ this._walletClient = null;
237
+ }
238
+ }
239
+ // ═══════════════════════════════════════════════════════
240
+ // VALIDATION
241
+ // ═══════════════════════════════════════════════════════
242
+ /**
243
+ * Validate an action against all active on-chain policies.
244
+ * This is an off-chain simulation — saves gas by catching rejections before submission.
245
+ *
246
+ * @param tokenId - Agent NFA token ID
247
+ * @param action - The action to validate
248
+ * @param callerOverride - Optional caller address override (defaults to operator)
249
+ */
250
+ async validate(tokenId, action, callerOverride) {
251
+ const caller = callerOverride ?? this._operatorAddress;
252
+ if (!caller) {
253
+ return { ok: false, reason: "No caller address \u2014 provide operatorPrivateKey or callerOverride" };
254
+ }
255
+ const vault = await this.getVault(tokenId);
256
+ try {
257
+ const [ok, reason] = await this.publicClient.readContract({
258
+ address: this.policyGuardAddress,
259
+ abi: PolicyGuardV4Abi,
260
+ functionName: "validate",
261
+ args: [
262
+ this.agentNfaAddress,
263
+ tokenId,
264
+ vault,
265
+ caller,
266
+ { target: action.target, value: action.value, data: action.data }
267
+ ]
268
+ });
269
+ return { ok, reason: reason || void 0 };
270
+ } catch (error) {
271
+ const message = error instanceof Error ? error.message : "Simulation reverted";
272
+ return { ok: false, reason: `Simulation reverted: ${message.slice(0, 300)}` };
273
+ }
274
+ }
275
+ // ═══════════════════════════════════════════════════════
276
+ // EXECUTION
277
+ // ═══════════════════════════════════════════════════════
278
+ /**
279
+ * Execute a single action through AgentNFA.
280
+ * Simulates first, then submits if validation passes.
281
+ *
282
+ * @param tokenId - Agent NFA token ID
283
+ * @param action - The action to execute
284
+ * @param skipValidation - Skip pre-flight validation (default: false)
285
+ */
286
+ async execute(tokenId, action, skipValidation = false) {
287
+ this.requireWallet();
288
+ if (!skipValidation) {
289
+ const sim = await this.validate(tokenId, action);
290
+ if (!sim.ok) {
291
+ throw new Error(`Policy rejected: ${sim.reason}`);
292
+ }
293
+ }
294
+ const hash = await this._walletClient.writeContract({
295
+ address: this.agentNfaAddress,
296
+ abi: AgentNFAAbi,
297
+ functionName: "execute",
298
+ args: [tokenId, { target: action.target, value: action.value, data: action.data }]
299
+ });
300
+ return { hash };
301
+ }
302
+ /**
303
+ * Execute multiple actions atomically via AgentNFA.executeBatch().
304
+ * Useful for approve+swap in a single transaction.
305
+ *
306
+ * @param tokenId - Agent NFA token ID
307
+ * @param actions - Array of actions to execute
308
+ * @param skipValidation - Skip per-action pre-flight validation (default: false)
309
+ */
310
+ async executeBatch(tokenId, actions, skipValidation = false) {
311
+ this.requireWallet();
312
+ if (!skipValidation) {
313
+ for (const action of actions) {
314
+ const sim = await this.validate(tokenId, action);
315
+ if (!sim.ok) {
316
+ throw new Error(`Policy rejected action (target=${action.target}): ${sim.reason}`);
317
+ }
318
+ }
319
+ }
320
+ const hash = await this._walletClient.writeContract({
321
+ address: this.agentNfaAddress,
322
+ abi: AgentNFAAbi,
323
+ functionName: "executeBatch",
324
+ args: [
325
+ tokenId,
326
+ actions.map((a) => ({ target: a.target, value: a.value, data: a.data }))
327
+ ]
328
+ });
329
+ return { hash };
330
+ }
331
+ // ═══════════════════════════════════════════════════════
332
+ // POLICY QUERIES
333
+ // ═══════════════════════════════════════════════════════
334
+ /**
335
+ * Get all active policies for an agent instance, with type info.
336
+ *
337
+ * @param tokenId - Agent NFA token ID
338
+ * @returns Array of PolicyInfo objects
339
+ */
340
+ async getPolicies(tokenId) {
341
+ const addresses = await this.publicClient.readContract({
342
+ address: this.policyGuardAddress,
343
+ abi: PolicyGuardV4Abi,
344
+ functionName: "getActivePolicies",
345
+ args: [tokenId]
346
+ });
347
+ const results = [];
348
+ for (const addr of addresses) {
349
+ try {
350
+ const [policyType, renterConfigurable] = await Promise.all([
351
+ this.publicClient.readContract({
352
+ address: addr,
353
+ abi: IPolicyAbi,
354
+ functionName: "policyType"
355
+ }),
356
+ this.publicClient.readContract({
357
+ address: addr,
358
+ abi: IPolicyAbi,
359
+ functionName: "renterConfigurable"
360
+ })
361
+ ]);
362
+ const typeName = POLICY_TYPE_NAMES[policyType] ?? "unknown";
363
+ results.push({
364
+ address: addr,
365
+ policyType,
366
+ policyTypeName: typeName,
367
+ renterConfigurable
368
+ });
369
+ } catch {
370
+ results.push({
371
+ address: addr,
372
+ policyType: "0x",
373
+ policyTypeName: "unknown",
374
+ renterConfigurable: false
375
+ });
376
+ }
377
+ }
378
+ return results;
379
+ }
380
+ // ═══════════════════════════════════════════════════════
381
+ // OPERATOR PERMIT
382
+ // ═══════════════════════════════════════════════════════
383
+ /**
384
+ * Read the current operator state for a token — useful for constructing permits.
385
+ *
386
+ * @param tokenId - Agent NFA token ID
387
+ * @returns Current operator, renter, vault, and nonce info
388
+ */
389
+ async getOperatorState(tokenId) {
390
+ const [operator, operatorExpires, renter, renterExpires, vault, operatorNonce] = await Promise.all([
391
+ this.publicClient.readContract({
392
+ address: this.agentNfaAddress,
393
+ abi: AgentNFAAbi,
394
+ functionName: "operatorOf",
395
+ args: [tokenId]
396
+ }),
397
+ this.publicClient.readContract({
398
+ address: this.agentNfaAddress,
399
+ abi: AgentNFAAbi,
400
+ functionName: "operatorExpiresOf",
401
+ args: [tokenId]
402
+ }),
403
+ this.publicClient.readContract({
404
+ address: this.agentNfaAddress,
405
+ abi: AgentNFAAbi,
406
+ functionName: "userOf",
407
+ args: [tokenId]
408
+ }),
409
+ this.publicClient.readContract({
410
+ address: this.agentNfaAddress,
411
+ abi: AgentNFAAbi,
412
+ functionName: "userExpires",
413
+ args: [tokenId]
414
+ }),
415
+ this.publicClient.readContract({
416
+ address: this.agentNfaAddress,
417
+ abi: AgentNFAAbi,
418
+ functionName: "accountOf",
419
+ args: [tokenId]
420
+ }),
421
+ this.publicClient.readContract({
422
+ address: this.agentNfaAddress,
423
+ abi: AgentNFAAbi,
424
+ functionName: "operatorNonceOf",
425
+ args: [tokenId]
426
+ })
427
+ ]);
428
+ return { operator, operatorExpires, renter, renterExpires, vault, operatorNonce };
429
+ }
430
+ /**
431
+ * Submit a signed OperatorPermit on-chain to authorize this runner as operator.
432
+ *
433
+ * @param permit - The OperatorPermit struct
434
+ * @param signature - EIP-712 signature from the renter
435
+ */
436
+ async setOperatorWithPermit(permit, signature) {
437
+ this.requireWallet();
438
+ const hash = await this._walletClient.writeContract({
439
+ address: this.agentNfaAddress,
440
+ abi: AgentNFAAbi,
441
+ functionName: "setOperatorWithSig",
442
+ args: [
443
+ {
444
+ tokenId: permit.tokenId,
445
+ renter: permit.renter,
446
+ operator: permit.operator,
447
+ expires: permit.expires,
448
+ nonce: permit.nonce,
449
+ deadline: permit.deadline
450
+ },
451
+ signature
452
+ ]
453
+ });
454
+ return { hash };
455
+ }
456
+ /**
457
+ * Clear the operator permit for a token.
458
+ *
459
+ * @param tokenId - Agent NFA token ID
460
+ */
461
+ async clearOperator(tokenId) {
462
+ this.requireWallet();
463
+ const hash = await this._walletClient.writeContract({
464
+ address: this.agentNfaAddress,
465
+ abi: AgentNFAAbi,
466
+ functionName: "clearOperator",
467
+ args: [tokenId]
468
+ });
469
+ return { hash };
470
+ }
471
+ // ═══════════════════════════════════════════════════════
472
+ // HELPERS
473
+ // ═══════════════════════════════════════════════════════
474
+ /**
475
+ * Get the vault (AgentAccount) address for a token.
476
+ */
477
+ async getVault(tokenId) {
478
+ return await this.publicClient.readContract({
479
+ address: this.agentNfaAddress,
480
+ abi: AgentNFAAbi,
481
+ functionName: "accountOf",
482
+ args: [tokenId]
483
+ });
484
+ }
485
+ /**
486
+ * Get the operator address configured for this client.
487
+ */
488
+ getOperatorAddress() {
489
+ return this._operatorAddress;
490
+ }
491
+ requireWallet() {
492
+ if (!this._walletClient) {
493
+ throw new Error(
494
+ "PolicyClient: operatorPrivateKey is required for write operations (execute, setOperator)"
495
+ );
496
+ }
497
+ }
498
+ };
499
+
500
+ export {
501
+ PolicyClient
502
+ };