@obelyzk/sdk 1.4.0 → 1.5.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,338 @@
1
+ // src/firewall/client.ts
2
+ import { RpcProvider, Contract, CallData } from "starknet";
3
+ var AgentFirewallSDK = class {
4
+ config;
5
+ provider;
6
+ constructor(config) {
7
+ this.config = config;
8
+ this.provider = new RpcProvider({ nodeUrl: config.rpcUrl });
9
+ }
10
+ // ── Classification ─────────────────────────────────────────────────
11
+ /**
12
+ * Classify a transaction through the ZKML classifier.
13
+ *
14
+ * Sends the transaction features to the prove-server, which runs
15
+ * the MLP classifier and generates a GKR+STARK proof. Returns the
16
+ * proven threat score and decision.
17
+ *
18
+ * Does NOT submit anything on-chain — use `evaluateAction()` for
19
+ * the full flow including on-chain submission.
20
+ */
21
+ async classify(tx) {
22
+ const headers = {
23
+ "Content-Type": "application/json"
24
+ };
25
+ if (this.config.apiKey) {
26
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
27
+ }
28
+ const body = {
29
+ target: tx.target,
30
+ value: tx.value || "0",
31
+ selector: tx.selector || "0x0",
32
+ calldata: tx.calldata || "0x",
33
+ agent_trust_score: tx.agentTrustScore || 0,
34
+ agent_strikes: tx.agentStrikes || 0,
35
+ agent_age_blocks: tx.agentAgeBlocks || 0,
36
+ target_verified: tx.targetVerified || false,
37
+ target_is_proxy: tx.targetIsProxy || false,
38
+ target_has_source: tx.targetHasSource || false,
39
+ target_interaction_count: tx.targetInteractionCount || 0,
40
+ tx_frequency: tx.txFrequency || 0,
41
+ unique_targets_24h: tx.uniqueTargets24h || 0,
42
+ avg_value_24h: tx.avgValue24h || 0,
43
+ max_value_24h: tx.maxValue24h || 0
44
+ };
45
+ const response = await fetch(`${this.config.proverUrl}/api/v1/classify`, {
46
+ method: "POST",
47
+ headers,
48
+ body: JSON.stringify(body)
49
+ });
50
+ if (!response.ok) {
51
+ const error = await response.json().catch(() => ({ error: response.statusText }));
52
+ throw new Error(`Classification failed (${response.status}): ${error.error || response.statusText}`);
53
+ }
54
+ const data = await response.json();
55
+ return {
56
+ requestId: data.request_id,
57
+ decision: data.decision,
58
+ threatScore: data.threat_score,
59
+ scores: data.scores,
60
+ ioCommitment: data.io_commitment,
61
+ policyCommitment: data.policy_commitment,
62
+ proveTimeMs: data.prove_time_ms
63
+ };
64
+ }
65
+ // ── Agent Management ───────────────────────────────────────────────
66
+ /**
67
+ * Register a new agent on the firewall contract.
68
+ * The calling account becomes the agent owner.
69
+ */
70
+ async registerAgent(agentId) {
71
+ this.requireAccount();
72
+ const tx = await this.config.account.execute({
73
+ contractAddress: this.config.firewallContract,
74
+ entrypoint: "register_agent",
75
+ calldata: CallData.compile({ agent_id: agentId })
76
+ });
77
+ await this.provider.waitForTransaction(tx.transaction_hash);
78
+ return tx.transaction_hash;
79
+ }
80
+ /** Deactivate an agent (owner or contract admin). */
81
+ async deactivateAgent(agentId) {
82
+ this.requireAccount();
83
+ const tx = await this.config.account.execute({
84
+ contractAddress: this.config.firewallContract,
85
+ entrypoint: "deactivate_agent",
86
+ calldata: CallData.compile({ agent_id: agentId })
87
+ });
88
+ await this.provider.waitForTransaction(tx.transaction_hash);
89
+ return tx.transaction_hash;
90
+ }
91
+ /** Reactivate an agent and reset strikes (agent owner only). */
92
+ async reactivateAgent(agentId) {
93
+ this.requireAccount();
94
+ const tx = await this.config.account.execute({
95
+ contractAddress: this.config.firewallContract,
96
+ entrypoint: "reactivate_agent",
97
+ calldata: CallData.compile({ agent_id: agentId })
98
+ });
99
+ await this.provider.waitForTransaction(tx.transaction_hash);
100
+ return tx.transaction_hash;
101
+ }
102
+ // ── Queries ────────────────────────────────────────────────────────
103
+ /** Get the full status of an agent. */
104
+ async getAgentStatus(agentId) {
105
+ const contract = new Contract(
106
+ FIREWALL_ABI,
107
+ this.config.firewallContract,
108
+ this.provider
109
+ );
110
+ const [registered, active, trustScore, strikes, trusted] = await Promise.all([
111
+ contract.is_agent_registered(agentId),
112
+ contract.is_agent_active(agentId),
113
+ contract.get_trust_score(agentId),
114
+ contract.get_strikes(agentId),
115
+ contract.is_trusted(agentId)
116
+ ]);
117
+ return {
118
+ registered,
119
+ active,
120
+ trustScore: Number(trustScore),
121
+ strikes: Number(strikes),
122
+ trusted
123
+ };
124
+ }
125
+ /** Check if a specific action has been approved. */
126
+ async isActionApproved(actionId) {
127
+ const contract = new Contract(
128
+ FIREWALL_ABI,
129
+ this.config.firewallContract,
130
+ this.provider
131
+ );
132
+ return contract.is_action_approved(actionId);
133
+ }
134
+ /** Check if an agent is trusted. */
135
+ async isAgentTrusted(agentId) {
136
+ const contract = new Contract(
137
+ FIREWALL_ABI,
138
+ this.config.firewallContract,
139
+ this.provider
140
+ );
141
+ return contract.is_trusted(agentId);
142
+ }
143
+ /** Get the decision for an action (0=pending, 1=approved, 2=escalated, 3=blocked). */
144
+ async getActionDecision(actionId) {
145
+ const contract = new Contract(
146
+ FIREWALL_ABI,
147
+ this.config.firewallContract,
148
+ this.provider
149
+ );
150
+ return Number(await contract.get_action_decision(actionId));
151
+ }
152
+ /** Get the threat score for a resolved action. */
153
+ async getActionThreatScore(actionId) {
154
+ const contract = new Contract(
155
+ FIREWALL_ABI,
156
+ this.config.firewallContract,
157
+ this.provider
158
+ );
159
+ return Number(await contract.get_action_threat_score(actionId));
160
+ }
161
+ /** Get the IO commitment for an action. */
162
+ async getActionIoCommitment(actionId) {
163
+ const contract = new Contract(
164
+ FIREWALL_ABI,
165
+ this.config.firewallContract,
166
+ this.provider
167
+ );
168
+ const result = await contract.get_action_io_commitment(actionId);
169
+ return `0x${BigInt(result).toString(16)}`;
170
+ }
171
+ // ── On-Chain Actions ────────────────────────────────────────────────
172
+ /**
173
+ * Submit a pending action to the firewall contract.
174
+ * Returns the action_id assigned by the contract.
175
+ */
176
+ async submitAction(agentId, target, value, selector, ioCommitment) {
177
+ this.requireAccount();
178
+ const tx = await this.config.account.execute({
179
+ contractAddress: this.config.firewallContract,
180
+ entrypoint: "submit_action",
181
+ calldata: CallData.compile({
182
+ agent_id: agentId,
183
+ target,
184
+ value,
185
+ selector,
186
+ io_commitment: ioCommitment
187
+ })
188
+ });
189
+ const receipt = await this.provider.waitForTransaction(tx.transaction_hash);
190
+ let actionId = 0;
191
+ const events = receipt.events;
192
+ if (Array.isArray(events)) {
193
+ for (const event of events) {
194
+ if (event.data && event.data.length >= 2) {
195
+ const candidateId = Number(BigInt(event.data[0]));
196
+ if (candidateId > 0) {
197
+ actionId = candidateId;
198
+ break;
199
+ }
200
+ }
201
+ }
202
+ }
203
+ return { actionId, txHash: tx.transaction_hash };
204
+ }
205
+ /**
206
+ * Resolve a pending action with a verified ZKML proof.
207
+ * The proof must already be verified on the ObelyskVerifier contract.
208
+ */
209
+ async resolveAction(actionId, proofHash, originalIoLen, packedRawIo) {
210
+ this.requireAccount();
211
+ const tx = await this.config.account.execute({
212
+ contractAddress: this.config.firewallContract,
213
+ entrypoint: "resolve_action_with_proof",
214
+ calldata: CallData.compile({
215
+ action_id: actionId,
216
+ proof_hash: proofHash,
217
+ original_io_len: originalIoLen,
218
+ packed_raw_io: packedRawIo
219
+ })
220
+ });
221
+ const receipt = await this.provider.waitForTransaction(tx.transaction_hash);
222
+ let decision = "approve";
223
+ let threatScore = 0;
224
+ const resolveEvents = receipt.events;
225
+ if (Array.isArray(resolveEvents)) {
226
+ for (const event of resolveEvents) {
227
+ if (event.data && event.data.length >= 4) {
228
+ const decisionCode = Number(BigInt(event.data[2]));
229
+ threatScore = Number(BigInt(event.data[3]));
230
+ if (decisionCode === 1) decision = "approve";
231
+ else if (decisionCode === 2) decision = "escalate";
232
+ else if (decisionCode === 3) decision = "block";
233
+ break;
234
+ }
235
+ }
236
+ }
237
+ return { decision, threatScore, txHash: tx.transaction_hash };
238
+ }
239
+ /** Approve an escalated action (human-in-the-loop). */
240
+ async approveEscalated(actionId) {
241
+ this.requireAccount();
242
+ const tx = await this.config.account.execute({
243
+ contractAddress: this.config.firewallContract,
244
+ entrypoint: "approve_escalated",
245
+ calldata: CallData.compile({ action_id: actionId })
246
+ });
247
+ await this.provider.waitForTransaction(tx.transaction_hash);
248
+ return tx.transaction_hash;
249
+ }
250
+ /** Reject an escalated action and add a strike. */
251
+ async rejectEscalated(actionId) {
252
+ this.requireAccount();
253
+ const tx = await this.config.account.execute({
254
+ contractAddress: this.config.firewallContract,
255
+ entrypoint: "reject_escalated",
256
+ calldata: CallData.compile({ action_id: actionId })
257
+ });
258
+ await this.provider.waitForTransaction(tx.transaction_hash);
259
+ return tx.transaction_hash;
260
+ }
261
+ // ── Helpers ────────────────────────────────────────────────────────
262
+ requireAccount() {
263
+ if (!this.config.account) {
264
+ throw new Error(
265
+ "Account required for write operations. Pass `account` in FirewallConfig."
266
+ );
267
+ }
268
+ }
269
+ };
270
+ var FIREWALL_ABI = [
271
+ {
272
+ name: "is_agent_registered",
273
+ type: "function",
274
+ inputs: [{ name: "agent_id", type: "felt" }],
275
+ outputs: [{ type: "felt" }],
276
+ state_mutability: "view"
277
+ },
278
+ {
279
+ name: "is_agent_active",
280
+ type: "function",
281
+ inputs: [{ name: "agent_id", type: "felt" }],
282
+ outputs: [{ type: "felt" }],
283
+ state_mutability: "view"
284
+ },
285
+ {
286
+ name: "get_trust_score",
287
+ type: "function",
288
+ inputs: [{ name: "agent_id", type: "felt" }],
289
+ outputs: [{ type: "felt" }],
290
+ state_mutability: "view"
291
+ },
292
+ {
293
+ name: "get_strikes",
294
+ type: "function",
295
+ inputs: [{ name: "agent_id", type: "felt" }],
296
+ outputs: [{ type: "felt" }],
297
+ state_mutability: "view"
298
+ },
299
+ {
300
+ name: "is_trusted",
301
+ type: "function",
302
+ inputs: [{ name: "agent_id", type: "felt" }],
303
+ outputs: [{ type: "felt" }],
304
+ state_mutability: "view"
305
+ },
306
+ {
307
+ name: "is_action_approved",
308
+ type: "function",
309
+ inputs: [{ name: "action_id", type: "felt" }],
310
+ outputs: [{ type: "felt" }],
311
+ state_mutability: "view"
312
+ },
313
+ {
314
+ name: "get_action_decision",
315
+ type: "function",
316
+ inputs: [{ name: "action_id", type: "felt" }],
317
+ outputs: [{ type: "felt" }],
318
+ state_mutability: "view"
319
+ },
320
+ {
321
+ name: "get_action_threat_score",
322
+ type: "function",
323
+ inputs: [{ name: "action_id", type: "felt" }],
324
+ outputs: [{ type: "felt" }],
325
+ state_mutability: "view"
326
+ },
327
+ {
328
+ name: "get_action_io_commitment",
329
+ type: "function",
330
+ inputs: [{ name: "action_id", type: "felt" }],
331
+ outputs: [{ type: "felt" }],
332
+ state_mutability: "view"
333
+ }
334
+ ];
335
+
336
+ export {
337
+ AgentFirewallSDK
338
+ };
@@ -0,0 +1,42 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+ var __commonJS = (cb, mod) => function __require2() {
14
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
15
+ };
16
+ var __export = (target, all) => {
17
+ for (var name in all)
18
+ __defProp(target, name, { get: all[name], enumerable: true });
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from === "object" || typeof from === "function") {
22
+ for (let key of __getOwnPropNames(from))
23
+ if (!__hasOwnProp.call(to, key) && key !== except)
24
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
25
+ }
26
+ return to;
27
+ };
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
29
+ // If the importer is in node compatibility mode or this is not an ESM
30
+ // file that has been converted to a CommonJS file using a Babel-
31
+ // compatible transform (i.e. "__esModule" has not been set), then set
32
+ // "default" to the CommonJS "module.exports" for node compatibility.
33
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
34
+ mod
35
+ ));
36
+
37
+ export {
38
+ __require,
39
+ __commonJS,
40
+ __export,
41
+ __toESM
42
+ };