@obelyzk/sdk 1.4.0 → 1.6.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 +7 -7
- package/dist/chunk-ASKP7TIW.mjs +153 -0
- package/dist/chunk-DGYMDV5X.mjs +153 -0
- package/dist/chunk-EHI6MQFS.mjs +566 -0
- package/dist/chunk-G3GLKFP5.mjs +0 -0
- package/dist/chunk-GK4FKSZ4.mjs +697 -0
- package/dist/chunk-NQ4E7ULF.mjs +338 -0
- package/dist/chunk-XGB3TDIC.mjs +42 -0
- package/dist/chunk-Y4PBMUWM.mjs +533 -0
- package/dist/client-DFxKbDns.d.mts +199 -0
- package/dist/client-DFxKbDns.d.ts +199 -0
- package/dist/firewall/index.d.mts +236 -130
- package/dist/firewall/index.d.ts +236 -130
- package/dist/firewall/index.js +479 -2
- package/dist/firewall/index.mjs +12 -4
- package/dist/index.d.mts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +231 -6
- package/dist/index.mjs +54 -9
- package/dist/mcp-policy/index.d.mts +1 -0
- package/dist/mcp-policy/index.d.ts +1 -0
- package/dist/mcp-policy/index.js +27903 -0
- package/dist/mcp-policy/index.mjs +27351 -0
- package/dist/obelysk/index.mjs +2 -2
- package/dist/privacy/index.mjs +2 -2
- package/dist/react/index.mjs +2 -2
- package/examples/.claude/rules/starknet.md +17 -0
- package/examples/CLAUDE.md +59 -0
- package/examples/claude-settings.json +52 -0
- package/examples/test_confidential_swap_api.ts +313 -0
- package/package.json +12 -4
- package/src/hooks/post-tool-use.sh +116 -0
- package/src/hooks/pre-tool-use.sh +112 -0
- package/src/hooks/session-start.sh +50 -0
package/dist/obelysk/index.mjs
CHANGED
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
ecMul,
|
|
9
9
|
invMod,
|
|
10
10
|
randomScalar
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-GK4FKSZ4.mjs";
|
|
12
12
|
import {
|
|
13
13
|
__require
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-XGB3TDIC.mjs";
|
|
15
15
|
|
|
16
16
|
// src/obelysk/client.ts
|
|
17
17
|
import { RpcProvider } from "starknet";
|
package/dist/privacy/index.mjs
CHANGED
package/dist/react/index.mjs
CHANGED
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
WorkersClient,
|
|
17
17
|
getContractsForNetwork
|
|
18
18
|
} from "../chunk-LXJT3QK6.mjs";
|
|
19
|
-
import "../chunk-
|
|
20
|
-
import "../chunk-
|
|
19
|
+
import "../chunk-GK4FKSZ4.mjs";
|
|
20
|
+
import "../chunk-XGB3TDIC.mjs";
|
|
21
21
|
|
|
22
22
|
// src/react/providers/BitSageProvider.tsx
|
|
23
23
|
import {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "scripts/**/*.mjs"
|
|
4
|
+
- "scripts/**/*.ts"
|
|
5
|
+
- "deployment/**"
|
|
6
|
+
- "contracts/**"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Starknet Transaction Rules
|
|
10
|
+
|
|
11
|
+
Before ANY on-chain transaction in these paths:
|
|
12
|
+
|
|
13
|
+
1. Call `obelyzk_classify` with the target contract address, value, and function selector.
|
|
14
|
+
2. If the decision is `escalate` or `block`, stop and explain the threat score.
|
|
15
|
+
3. Include the `io_commitment` in the commit message for the audit trail.
|
|
16
|
+
4. Never use `--skip-policy` or set `STWO_SKIP_POLICY_COMMITMENT=1`.
|
|
17
|
+
5. Never call `starkli invoke` or `sncast invoke` without prior classification.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
## Agent Safety Policy
|
|
2
|
+
|
|
3
|
+
This project uses ObelyZK cryptographic policy enforcement. All on-chain actions
|
|
4
|
+
are classified by a ZKML model before execution. The classifier generates a
|
|
5
|
+
GKR+STARK proof binding the threat score to the specific transaction features
|
|
6
|
+
and policy configuration.
|
|
7
|
+
|
|
8
|
+
### Tools Available
|
|
9
|
+
|
|
10
|
+
- `obelyzk_classify` — Classify a transaction before executing it. Returns a
|
|
11
|
+
proven threat score (0-100000) and decision (approve/escalate/block).
|
|
12
|
+
- `obelyzk_agent_status` — Check your agent's trust score, strike count, and
|
|
13
|
+
whether you are trusted by the firewall contract.
|
|
14
|
+
- `obelyzk_check_action` — Query the status of a previously submitted action.
|
|
15
|
+
- `obelyzk_health` — Check prover server and firewall contract availability.
|
|
16
|
+
- `obelyzk_get_policy` — Inspect the active policy configuration and commitment hash.
|
|
17
|
+
- `obelyzk_list_models` — List available models on the prover server.
|
|
18
|
+
- `obelyzk_prove_inference` — Submit an async proof generation job for any model.
|
|
19
|
+
- `obelyzk_verify_proof` — Check if a proof has been verified on-chain.
|
|
20
|
+
- `obelyzk_register_agent` — Register a new agent on the firewall contract.
|
|
21
|
+
- `obelyzk_submit_action` — Submit a pending action for on-chain classification.
|
|
22
|
+
- `obelyzk_resolve_action` — Resolve a pending action with a verified ZKML proof.
|
|
23
|
+
- `obelyzk_approve_escalated` — Approve an escalated action (human-in-the-loop).
|
|
24
|
+
- `obelyzk_reject_escalated` — Reject an escalated action and add a strike.
|
|
25
|
+
|
|
26
|
+
### Required Workflow
|
|
27
|
+
|
|
28
|
+
Before ANY on-chain transaction (`starkli invoke`, `sncast invoke`, contract
|
|
29
|
+
deployments, token transfers, approvals):
|
|
30
|
+
|
|
31
|
+
1. Call `obelyzk_classify` with the target address, value, and selector.
|
|
32
|
+
2. If decision is `block` — do NOT execute. Explain the threat score to the user.
|
|
33
|
+
3. If decision is `escalate` — ask the user for explicit approval before proceeding.
|
|
34
|
+
4. If decision is `approve` — safe to execute.
|
|
35
|
+
5. Include the `io_commitment` from the classify result in any commit messages
|
|
36
|
+
or logs for audit trail purposes.
|
|
37
|
+
|
|
38
|
+
### Decision Thresholds
|
|
39
|
+
|
|
40
|
+
| Score Range | Decision | Action |
|
|
41
|
+
|-------------|----------|--------|
|
|
42
|
+
| 0 - 40,000 | approve | Execute immediately |
|
|
43
|
+
| 40,001 - 70,000 | escalate | Requires human review |
|
|
44
|
+
| 70,001 - 100,000 | block | Reject, do not execute |
|
|
45
|
+
|
|
46
|
+
### Trust Score
|
|
47
|
+
|
|
48
|
+
Your agent's trust score is updated via asymmetric EMA after each resolved action:
|
|
49
|
+
- Bad actions raise the score quickly (alpha = 0.5)
|
|
50
|
+
- Good actions lower the score slowly (alpha = 0.1)
|
|
51
|
+
- At 5 strikes, the agent is automatically frozen
|
|
52
|
+
|
|
53
|
+
Check your status with `obelyzk_agent_status` if actions start failing.
|
|
54
|
+
|
|
55
|
+
### Restrictions
|
|
56
|
+
|
|
57
|
+
- Do NOT set `STWO_SKIP_POLICY_COMMITMENT=1` in any environment.
|
|
58
|
+
- Do NOT bypass the classifier by calling contracts directly without classification.
|
|
59
|
+
- Do NOT modify firewall thresholds without explicit user authorization.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"obelyzk-policy": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": ["ts-node", "./node_modules/@obelyzk/sdk/src/mcp-policy/index.ts"],
|
|
6
|
+
"env": {
|
|
7
|
+
"PROVER_URL": "http://localhost:8080",
|
|
8
|
+
"STARKNET_RPC": "https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_7/YOUR_KEY",
|
|
9
|
+
"FIREWALL_CONTRACT": "0x_YOUR_FIREWALL_CONTRACT_ADDRESS",
|
|
10
|
+
"VERIFIER_CONTRACT": "0x_YOUR_VERIFIER_CONTRACT_ADDRESS",
|
|
11
|
+
"PROVER_API_KEY": "",
|
|
12
|
+
"DEPLOYER_PRIVKEY": "",
|
|
13
|
+
"DEPLOYER_ADDRESS": ""
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"hooks": {
|
|
18
|
+
"PreToolUse": [
|
|
19
|
+
{
|
|
20
|
+
"matcher": "Bash",
|
|
21
|
+
"hooks": [
|
|
22
|
+
{
|
|
23
|
+
"type": "command",
|
|
24
|
+
"command": "./node_modules/@obelyzk/sdk/src/hooks/pre-tool-use.sh"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"PostToolUse": [
|
|
30
|
+
{
|
|
31
|
+
"matcher": "Bash|mcp__obelyzk-policy__.*",
|
|
32
|
+
"hooks": [
|
|
33
|
+
{
|
|
34
|
+
"type": "command",
|
|
35
|
+
"command": "./node_modules/@obelyzk/sdk/src/hooks/post-tool-use.sh",
|
|
36
|
+
"async": true
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"SessionStart": [
|
|
42
|
+
{
|
|
43
|
+
"hooks": [
|
|
44
|
+
{
|
|
45
|
+
"type": "command",
|
|
46
|
+
"command": "./node_modules/@obelyzk/sdk/src/hooks/session-start.sh"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Confidential Swap API - End-to-End Flow
|
|
3
|
+
*
|
|
4
|
+
* Tests the privacy swap API endpoints without requiring blockchain credentials.
|
|
5
|
+
* Verifies proof generation, order creation, and order taking flows.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const API_URL = process.env.API_URL || 'http://localhost:8090';
|
|
9
|
+
|
|
10
|
+
interface ElGamalCiphertext {
|
|
11
|
+
c1_x: string;
|
|
12
|
+
c1_y: string;
|
|
13
|
+
c2_x: string;
|
|
14
|
+
c2_y: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ProofBundle {
|
|
18
|
+
rangeProof: {
|
|
19
|
+
commitment: { x: string; y: string };
|
|
20
|
+
challenge: string;
|
|
21
|
+
response: string;
|
|
22
|
+
};
|
|
23
|
+
rateProof: {
|
|
24
|
+
rateCommitment: { x: string; y: string };
|
|
25
|
+
challenge: string;
|
|
26
|
+
responseGive: string;
|
|
27
|
+
responseRate: string;
|
|
28
|
+
responseBlinding: string;
|
|
29
|
+
};
|
|
30
|
+
balanceProof: {
|
|
31
|
+
balanceCommitment: { x: string; y: string };
|
|
32
|
+
challenge: string;
|
|
33
|
+
response: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function log(msg: string, type: 'info' | 'success' | 'error' | 'header' = 'info') {
|
|
38
|
+
const colors = {
|
|
39
|
+
info: '\x1b[36m',
|
|
40
|
+
success: '\x1b[32m',
|
|
41
|
+
error: '\x1b[31m',
|
|
42
|
+
header: '\x1b[35m',
|
|
43
|
+
};
|
|
44
|
+
console.log(`${colors[type]}${msg}\x1b[0m`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function testHealthEndpoint(): Promise<boolean> {
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(`${API_URL}/api/v1/privacy/health`);
|
|
50
|
+
const data = await response.json();
|
|
51
|
+
log(` Status: ${data.status}`, data.status === 'healthy' ? 'success' : 'error');
|
|
52
|
+
log(` GPU Available: ${data.gpu_available}`, 'info');
|
|
53
|
+
log(` Version: ${data.version}`, 'info');
|
|
54
|
+
return data.status === 'healthy';
|
|
55
|
+
} catch (e) {
|
|
56
|
+
log(` Health check failed: ${(e as Error).message}`, 'error');
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function testListOrders(): Promise<any[]> {
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetch(`${API_URL}/api/v1/privacy/orders`);
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
log(` Total orders: ${data.orders?.length || 0}`, 'success');
|
|
66
|
+
return data.orders || [];
|
|
67
|
+
} catch (e) {
|
|
68
|
+
log(` List orders failed: ${(e as Error).message}`, 'error');
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function testGenerateProof(giveAmount: string, wantAmount: string): Promise<ProofBundle | null> {
|
|
74
|
+
try {
|
|
75
|
+
const response = await fetch(`${API_URL}/api/v1/privacy/generate-swap-proof`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
body: JSON.stringify({
|
|
79
|
+
giveAsset: 0, // SAGE
|
|
80
|
+
wantAsset: 1, // USDC
|
|
81
|
+
giveAmount,
|
|
82
|
+
wantAmount,
|
|
83
|
+
rate: (BigInt(wantAmount) * BigInt('1000000000000000000') / BigInt(giveAmount)).toString(),
|
|
84
|
+
blindingFactor: '0x' + Math.random().toString(16).slice(2).padStart(64, '0'),
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error(`HTTP ${response.status}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const data = await response.json();
|
|
93
|
+
log(` Range proof: generated`, 'success');
|
|
94
|
+
log(` Rate proof: generated`, 'success');
|
|
95
|
+
log(` Balance proof: generated`, 'success');
|
|
96
|
+
return data;
|
|
97
|
+
} catch (e) {
|
|
98
|
+
log(` Proof generation failed: ${(e as Error).message}`, 'error');
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function testCreateOrder(proofs: ProofBundle): Promise<string | null> {
|
|
104
|
+
try {
|
|
105
|
+
// Generate mock encrypted amounts
|
|
106
|
+
const randomHex = () => '0x' + Math.random().toString(16).slice(2).padStart(64, '0');
|
|
107
|
+
|
|
108
|
+
const encryptedGive: ElGamalCiphertext = {
|
|
109
|
+
c1_x: randomHex(),
|
|
110
|
+
c1_y: randomHex(),
|
|
111
|
+
c2_x: randomHex(),
|
|
112
|
+
c2_y: randomHex(),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const encryptedWant: ElGamalCiphertext = {
|
|
116
|
+
c1_x: randomHex(),
|
|
117
|
+
c1_y: randomHex(),
|
|
118
|
+
c2_x: randomHex(),
|
|
119
|
+
c2_y: randomHex(),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const response = await fetch(`${API_URL}/api/v1/privacy/orders`, {
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: { 'Content-Type': 'application/json' },
|
|
125
|
+
body: JSON.stringify({
|
|
126
|
+
giveAsset: 0,
|
|
127
|
+
wantAsset: 1,
|
|
128
|
+
encryptedGive,
|
|
129
|
+
encryptedWant,
|
|
130
|
+
rateCommitment: randomHex(),
|
|
131
|
+
minFillPct: 0,
|
|
132
|
+
expiresIn: 604800,
|
|
133
|
+
proofs,
|
|
134
|
+
}),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (!response.ok) {
|
|
138
|
+
const text = await response.text();
|
|
139
|
+
log(` Create order returned ${response.status}: ${text.slice(0, 100)}`, 'info');
|
|
140
|
+
// May not be fully implemented - that's OK for API test
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const data = await response.json();
|
|
145
|
+
log(` Order created: ${data.orderId}`, 'success');
|
|
146
|
+
return data.orderId;
|
|
147
|
+
} catch (e) {
|
|
148
|
+
log(` Create order not implemented (expected): ${(e as Error).message}`, 'info');
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function testTakeOrder(orderId: string = '1'): Promise<boolean> {
|
|
154
|
+
try {
|
|
155
|
+
const randomHex = () => '0x' + Math.random().toString(16).slice(2).padStart(64, '0');
|
|
156
|
+
|
|
157
|
+
// Generate 64 bit commitments and responses for range proof
|
|
158
|
+
const bitCommitments = Array.from({ length: 64 }, () => ({
|
|
159
|
+
x: randomHex(),
|
|
160
|
+
y: randomHex(),
|
|
161
|
+
}));
|
|
162
|
+
const responses = Array.from({ length: 64 }, () => randomHex());
|
|
163
|
+
|
|
164
|
+
const response = await fetch(`${API_URL}/api/v1/privacy/orders/${orderId}/take`, {
|
|
165
|
+
method: 'POST',
|
|
166
|
+
headers: { 'Content-Type': 'application/json' },
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
// Match the Rust API schema: TakeOrderRequest
|
|
169
|
+
takerGive: {
|
|
170
|
+
c1: { x: randomHex(), y: randomHex() },
|
|
171
|
+
c2: { x: randomHex(), y: randomHex() },
|
|
172
|
+
},
|
|
173
|
+
takerWant: {
|
|
174
|
+
c1: { x: randomHex(), y: randomHex() },
|
|
175
|
+
c2: { x: randomHex(), y: randomHex() },
|
|
176
|
+
},
|
|
177
|
+
proofs: {
|
|
178
|
+
rangeProof: {
|
|
179
|
+
bitCommitments,
|
|
180
|
+
challenge: randomHex(),
|
|
181
|
+
responses,
|
|
182
|
+
numBits: 64,
|
|
183
|
+
},
|
|
184
|
+
rateProof: {
|
|
185
|
+
rateCommitment: { x: randomHex(), y: randomHex() },
|
|
186
|
+
challenge: randomHex(),
|
|
187
|
+
responseGive: randomHex(),
|
|
188
|
+
responseRate: randomHex(),
|
|
189
|
+
responseBlinding: randomHex(),
|
|
190
|
+
},
|
|
191
|
+
balanceProof: {
|
|
192
|
+
balanceCommitment: { x: randomHex(), y: randomHex() },
|
|
193
|
+
challenge: randomHex(),
|
|
194
|
+
response: randomHex(),
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const data = await response.json();
|
|
201
|
+
if (data.matchId) {
|
|
202
|
+
log(` Order taken! Match ID: ${data.matchId.slice(0, 20)}...`, 'success');
|
|
203
|
+
log(` Status: ${data.status}`, 'success');
|
|
204
|
+
return true;
|
|
205
|
+
} else if (data.error) {
|
|
206
|
+
log(` Take order error: ${data.error}`, 'error');
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
return false;
|
|
210
|
+
} catch (e) {
|
|
211
|
+
log(` Take order failed: ${(e as Error).message}`, 'error');
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function testGetBalance(): Promise<boolean> {
|
|
217
|
+
try {
|
|
218
|
+
const testAddress = '0x0759a4374389b0e3cfcc59d49310b6bc75bb12bbf8ce550eb5c2f026918bb344';
|
|
219
|
+
// Asset ID: 0=SAGE, 1=USDC, 2=STRK, 3=ETH
|
|
220
|
+
const assetId = 0; // SAGE
|
|
221
|
+
const response = await fetch(`${API_URL}/api/v1/privacy/balance/${testAddress}/${assetId}`);
|
|
222
|
+
const data = await response.json();
|
|
223
|
+
log(` Encrypted balance retrieved`, 'success');
|
|
224
|
+
log(` Has ciphertext: ${!!data.c1?.x}`, 'info');
|
|
225
|
+
return true;
|
|
226
|
+
} catch (e) {
|
|
227
|
+
log(` Get balance failed: ${(e as Error).message}`, 'error');
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function testSwapHistory(): Promise<boolean> {
|
|
233
|
+
try {
|
|
234
|
+
const testAddress = '0x0759a4374389b0e3cfcc59d49310b6bc75bb12bbf8ce550eb5c2f026918bb344';
|
|
235
|
+
const response = await fetch(`${API_URL}/api/v1/privacy/swaps/history/${testAddress}`);
|
|
236
|
+
const data = await response.json();
|
|
237
|
+
log(` Swap history entries: ${data.swaps?.length || 0}`, 'success');
|
|
238
|
+
return true;
|
|
239
|
+
} catch (e) {
|
|
240
|
+
log(` Get swap history failed: ${(e as Error).message}`, 'error');
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function main() {
|
|
246
|
+
log('\n╔══════════════════════════════════════════════════════════════════════╗', 'header');
|
|
247
|
+
log('║ CONFIDENTIAL SWAP API - End-to-End Test ║', 'header');
|
|
248
|
+
log('╚══════════════════════════════════════════════════════════════════════╝', 'header');
|
|
249
|
+
log(`\n API URL: ${API_URL}\n`, 'info');
|
|
250
|
+
|
|
251
|
+
const results: { test: string; passed: boolean }[] = [];
|
|
252
|
+
|
|
253
|
+
// Test 1: Health Check
|
|
254
|
+
log('\n=== Test 1: Health Check ===', 'header');
|
|
255
|
+
const healthOk = await testHealthEndpoint();
|
|
256
|
+
results.push({ test: 'Health Check', passed: healthOk });
|
|
257
|
+
|
|
258
|
+
if (!healthOk) {
|
|
259
|
+
log('\n Server not running or unhealthy. Aborting tests.', 'error');
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Test 2: List Orders
|
|
264
|
+
log('\n=== Test 2: List Orders ===', 'header');
|
|
265
|
+
const orders = await testListOrders();
|
|
266
|
+
results.push({ test: 'List Orders', passed: true });
|
|
267
|
+
|
|
268
|
+
// Test 3: Generate Proof
|
|
269
|
+
log('\n=== Test 3: Generate STWO Proof ===', 'header');
|
|
270
|
+
log(' Generating proof for: 100 SAGE -> 10 USDC', 'info');
|
|
271
|
+
const proofs = await testGenerateProof('100000000000000000000', '10000000');
|
|
272
|
+
results.push({ test: 'Generate Proof', passed: !!proofs });
|
|
273
|
+
|
|
274
|
+
// Test 4: Take Order (simulated)
|
|
275
|
+
log('\n=== Test 4: Take Order (Simulated) ===', 'header');
|
|
276
|
+
const takeOk = await testTakeOrder('1');
|
|
277
|
+
results.push({ test: 'Take Order', passed: takeOk });
|
|
278
|
+
|
|
279
|
+
// Test 5: Get Encrypted Balance
|
|
280
|
+
log('\n=== Test 5: Get Encrypted Balance ===', 'header');
|
|
281
|
+
const balanceOk = await testGetBalance();
|
|
282
|
+
results.push({ test: 'Get Balance', passed: balanceOk });
|
|
283
|
+
|
|
284
|
+
// Test 6: Get Swap History
|
|
285
|
+
log('\n=== Test 6: Get Swap History ===', 'header');
|
|
286
|
+
const historyOk = await testSwapHistory();
|
|
287
|
+
results.push({ test: 'Swap History', passed: historyOk });
|
|
288
|
+
|
|
289
|
+
// Summary
|
|
290
|
+
log('\n╔══════════════════════════════════════════════════════════════════════╗', 'header');
|
|
291
|
+
log('║ TEST SUMMARY ║', 'header');
|
|
292
|
+
log('╚══════════════════════════════════════════════════════════════════════╝', 'header');
|
|
293
|
+
|
|
294
|
+
const passed = results.filter(r => r.passed).length;
|
|
295
|
+
const total = results.length;
|
|
296
|
+
|
|
297
|
+
for (const r of results) {
|
|
298
|
+
log(` ${r.passed ? '✓' : '✗'} ${r.test}`, r.passed ? 'success' : 'error');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
log(`\n Result: ${passed}/${total} tests passed`, passed === total ? 'success' : 'error');
|
|
302
|
+
|
|
303
|
+
if (passed === total) {
|
|
304
|
+
log('\n Confidential Swap API is fully operational!', 'success');
|
|
305
|
+
log(' Ready for SDK integration and frontend testing.\n', 'success');
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
main().catch(e => {
|
|
310
|
+
log(`\nFatal error: ${e.message}`, 'error');
|
|
311
|
+
console.error(e);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obelyzk/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "ObelyZK SDK — verifiable ML inference on Starknet with recursive STARK proofs",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -30,11 +30,17 @@
|
|
|
30
30
|
"types": "./dist/obelysk/index.d.ts",
|
|
31
31
|
"import": "./dist/obelysk/index.mjs",
|
|
32
32
|
"require": "./dist/obelysk/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./mcp-policy": {
|
|
35
|
+
"types": "./dist/mcp-policy/index.d.ts",
|
|
36
|
+
"import": "./dist/mcp-policy/index.mjs",
|
|
37
|
+
"require": "./dist/mcp-policy/index.js"
|
|
33
38
|
}
|
|
34
39
|
},
|
|
35
40
|
"license": "MIT",
|
|
36
41
|
"bin": {
|
|
37
|
-
"bitsage-demo": "./bin/bitsage-demo.ts"
|
|
42
|
+
"bitsage-demo": "./bin/bitsage-demo.ts",
|
|
43
|
+
"obelyzk-policy-server": "./dist/mcp-policy/index.js"
|
|
38
44
|
},
|
|
39
45
|
"repository": {
|
|
40
46
|
"type": "git",
|
|
@@ -59,7 +65,7 @@
|
|
|
59
65
|
"zk-proofs"
|
|
60
66
|
],
|
|
61
67
|
"scripts": {
|
|
62
|
-
"build": "tsup src/index.ts src/privacy/index.ts src/react/index.ts src/obelysk/index.ts src/firewall/index.ts --format cjs,esm --dts",
|
|
68
|
+
"build": "tsup src/index.ts src/privacy/index.ts src/react/index.ts src/obelysk/index.ts src/firewall/index.ts src/mcp-policy/index.ts --format cjs,esm --dts",
|
|
63
69
|
"dev": "tsup src/index.ts src/privacy/index.ts src/react/index.ts src/obelysk/index.ts --format cjs,esm --dts --watch",
|
|
64
70
|
"lint": "eslint src/",
|
|
65
71
|
"test": "vitest",
|
|
@@ -93,13 +99,15 @@
|
|
|
93
99
|
},
|
|
94
100
|
"files": [
|
|
95
101
|
"dist",
|
|
102
|
+
"src/hooks",
|
|
103
|
+
"examples",
|
|
96
104
|
"README.md"
|
|
97
105
|
],
|
|
98
106
|
"publishConfig": {
|
|
99
107
|
"access": "public",
|
|
100
108
|
"registry": "https://registry.npmjs.org/"
|
|
101
109
|
},
|
|
102
|
-
"homepage": "https://obelysk.
|
|
110
|
+
"homepage": "https://obelysk.xyz",
|
|
103
111
|
"bugs": {
|
|
104
112
|
"url": "https://github.com/Bitsage-Network/bitsage-network/issues"
|
|
105
113
|
},
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ObelyZK PostToolUse Audit Hook
|
|
3
|
+
#
|
|
4
|
+
# Logs tool execution results for audit trail. Captures:
|
|
5
|
+
# - Tool name and timestamp
|
|
6
|
+
# - Resolved action decisions, threat scores, proof hashes
|
|
7
|
+
# - Transaction hashes from on-chain operations
|
|
8
|
+
#
|
|
9
|
+
# Install in .claude/settings.json:
|
|
10
|
+
# {
|
|
11
|
+
# "hooks": {
|
|
12
|
+
# "PostToolUse": [{
|
|
13
|
+
# "matcher": "Bash|mcp__obelyzk-policy__.*",
|
|
14
|
+
# "hooks": [{
|
|
15
|
+
# "type": "command",
|
|
16
|
+
# "command": "/path/to/post-tool-use.sh",
|
|
17
|
+
# "async": true
|
|
18
|
+
# }]
|
|
19
|
+
# }]
|
|
20
|
+
# }
|
|
21
|
+
# }
|
|
22
|
+
#
|
|
23
|
+
# Environment:
|
|
24
|
+
# OBELYZK_AUDIT_LOG - Path to audit log file (default: ~/.obelyzk/audit.jsonl)
|
|
25
|
+
#
|
|
26
|
+
# This hook is async (non-blocking) — it logs but never blocks tool execution.
|
|
27
|
+
|
|
28
|
+
set -euo pipefail
|
|
29
|
+
|
|
30
|
+
AUDIT_LOG="${OBELYZK_AUDIT_LOG:-$HOME/.obelyzk/audit.jsonl}"
|
|
31
|
+
|
|
32
|
+
# Ensure log directory exists
|
|
33
|
+
mkdir -p "$(dirname "$AUDIT_LOG")"
|
|
34
|
+
|
|
35
|
+
# Read hook context from stdin
|
|
36
|
+
INPUT=$(cat)
|
|
37
|
+
|
|
38
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null || echo "")
|
|
39
|
+
if [ -z "$TOOL_NAME" ]; then
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
44
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null || echo "unknown")
|
|
45
|
+
|
|
46
|
+
# Extract tool output if available
|
|
47
|
+
TOOL_OUTPUT=$(echo "$INPUT" | jq -r '.tool_output // empty' 2>/dev/null || echo "")
|
|
48
|
+
|
|
49
|
+
# Build audit entry based on tool type
|
|
50
|
+
case "$TOOL_NAME" in
|
|
51
|
+
mcp__obelyzk-policy__obelyzk_classify|mcp__obelyzk-policy__firewall_classify)
|
|
52
|
+
# Classification result
|
|
53
|
+
DECISION=$(echo "$TOOL_OUTPUT" | jq -r '.decision // empty' 2>/dev/null || echo "")
|
|
54
|
+
SCORE=$(echo "$TOOL_OUTPUT" | jq -r '.threat_score // 0' 2>/dev/null || echo "0")
|
|
55
|
+
IO_COMMIT=$(echo "$TOOL_OUTPUT" | jq -r '.io_commitment // empty' 2>/dev/null || echo "")
|
|
56
|
+
POLICY=$(echo "$TOOL_OUTPUT" | jq -r '.policy_commitment // empty' 2>/dev/null || echo "")
|
|
57
|
+
|
|
58
|
+
jq -nc \
|
|
59
|
+
--arg ts "$TIMESTAMP" \
|
|
60
|
+
--arg sid "$SESSION_ID" \
|
|
61
|
+
--arg tool "$TOOL_NAME" \
|
|
62
|
+
--arg decision "$DECISION" \
|
|
63
|
+
--argjson score "$SCORE" \
|
|
64
|
+
--arg io "$IO_COMMIT" \
|
|
65
|
+
--arg policy "$POLICY" \
|
|
66
|
+
'{timestamp: $ts, session: $sid, event: "classify", tool: $tool, decision: $decision, threat_score: $score, io_commitment: $io, policy_commitment: $policy}' \
|
|
67
|
+
>> "$AUDIT_LOG"
|
|
68
|
+
;;
|
|
69
|
+
|
|
70
|
+
mcp__obelyzk-policy__obelyzk_resolve_action|mcp__obelyzk-policy__firewall_resolve_action)
|
|
71
|
+
# Resolved action
|
|
72
|
+
ACTION_ID=$(echo "$TOOL_OUTPUT" | jq -r '.action_id // empty' 2>/dev/null || echo "")
|
|
73
|
+
DECISION=$(echo "$TOOL_OUTPUT" | jq -r '.decision // empty' 2>/dev/null || echo "")
|
|
74
|
+
SCORE=$(echo "$TOOL_OUTPUT" | jq -r '.threat_score // 0' 2>/dev/null || echo "0")
|
|
75
|
+
TX_HASH=$(echo "$TOOL_OUTPUT" | jq -r '.tx_hash // empty' 2>/dev/null || echo "")
|
|
76
|
+
|
|
77
|
+
jq -nc \
|
|
78
|
+
--arg ts "$TIMESTAMP" \
|
|
79
|
+
--arg sid "$SESSION_ID" \
|
|
80
|
+
--arg action "$ACTION_ID" \
|
|
81
|
+
--arg decision "$DECISION" \
|
|
82
|
+
--argjson score "$SCORE" \
|
|
83
|
+
--arg tx "$TX_HASH" \
|
|
84
|
+
'{timestamp: $ts, session: $sid, event: "resolve", action_id: $action, decision: $decision, threat_score: $score, tx_hash: $tx}' \
|
|
85
|
+
>> "$AUDIT_LOG"
|
|
86
|
+
;;
|
|
87
|
+
|
|
88
|
+
mcp__obelyzk-policy__obelyzk_submit_action|mcp__obelyzk-policy__firewall_submit_action)
|
|
89
|
+
# Submitted action
|
|
90
|
+
ACTION_ID=$(echo "$TOOL_OUTPUT" | jq -r '.action_id // empty' 2>/dev/null || echo "")
|
|
91
|
+
TX_HASH=$(echo "$TOOL_OUTPUT" | jq -r '.tx_hash // empty' 2>/dev/null || echo "")
|
|
92
|
+
|
|
93
|
+
jq -nc \
|
|
94
|
+
--arg ts "$TIMESTAMP" \
|
|
95
|
+
--arg sid "$SESSION_ID" \
|
|
96
|
+
--arg action "$ACTION_ID" \
|
|
97
|
+
--arg tx "$TX_HASH" \
|
|
98
|
+
'{timestamp: $ts, session: $sid, event: "submit", action_id: $action, tx_hash: $tx}' \
|
|
99
|
+
>> "$AUDIT_LOG"
|
|
100
|
+
;;
|
|
101
|
+
|
|
102
|
+
Bash)
|
|
103
|
+
# Log on-chain Bash commands
|
|
104
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null || echo "")
|
|
105
|
+
if echo "$COMMAND" | grep -qE '(starkli invoke|sncast invoke|starkli deploy)'; then
|
|
106
|
+
jq -nc \
|
|
107
|
+
--arg ts "$TIMESTAMP" \
|
|
108
|
+
--arg sid "$SESSION_ID" \
|
|
109
|
+
--arg cmd "$COMMAND" \
|
|
110
|
+
'{timestamp: $ts, session: $sid, event: "onchain_bash", command: $cmd}' \
|
|
111
|
+
>> "$AUDIT_LOG"
|
|
112
|
+
fi
|
|
113
|
+
;;
|
|
114
|
+
esac
|
|
115
|
+
|
|
116
|
+
exit 0
|