@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
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__require
|
|
3
|
+
} from "./chunk-XGB3TDIC.mjs";
|
|
4
|
+
|
|
5
|
+
// src/privacy/index.ts
|
|
6
|
+
var FIELD_PRIME = BigInt(
|
|
7
|
+
"0x800000000000011000000000000000000000000000000000000000000000001"
|
|
8
|
+
);
|
|
9
|
+
var CURVE_ORDER = BigInt(
|
|
10
|
+
"0x800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f"
|
|
11
|
+
);
|
|
12
|
+
var GENERATOR_G = {
|
|
13
|
+
x: BigInt("0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca"),
|
|
14
|
+
y: BigInt("0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f")
|
|
15
|
+
};
|
|
16
|
+
var GENERATOR_H = {
|
|
17
|
+
x: BigInt("0x4fa56f376c83db33f9dab2656558f3399099ec1de5e3018b7a6932dba8aa378"),
|
|
18
|
+
y: BigInt("0x3fa0984c931c9e38113e0c0e47e4401562761f92a7a23b45168f4e80ff5b54d")
|
|
19
|
+
};
|
|
20
|
+
var AssetId = /* @__PURE__ */ ((AssetId2) => {
|
|
21
|
+
AssetId2[AssetId2["SAGE"] = 0] = "SAGE";
|
|
22
|
+
AssetId2[AssetId2["USDC"] = 1] = "USDC";
|
|
23
|
+
AssetId2[AssetId2["STRK"] = 2] = "STRK";
|
|
24
|
+
AssetId2[AssetId2["ETH"] = 3] = "ETH";
|
|
25
|
+
AssetId2[AssetId2["BTC"] = 4] = "BTC";
|
|
26
|
+
return AssetId2;
|
|
27
|
+
})(AssetId || {});
|
|
28
|
+
function addMod(a, b, n = FIELD_PRIME) {
|
|
29
|
+
return (a % n + b % n) % n;
|
|
30
|
+
}
|
|
31
|
+
function subMod(a, b, n = FIELD_PRIME) {
|
|
32
|
+
const result = (a % n - b % n) % n;
|
|
33
|
+
return result < 0n ? result + n : result;
|
|
34
|
+
}
|
|
35
|
+
function mulMod(a, b, n = FIELD_PRIME) {
|
|
36
|
+
return a % n * (b % n) % n;
|
|
37
|
+
}
|
|
38
|
+
function powMod(base, exp, n = FIELD_PRIME) {
|
|
39
|
+
if (exp === 0n) return 1n;
|
|
40
|
+
let result = 1n;
|
|
41
|
+
base = base % n;
|
|
42
|
+
while (exp > 0n) {
|
|
43
|
+
if (exp % 2n === 1n) {
|
|
44
|
+
result = result * base % n;
|
|
45
|
+
}
|
|
46
|
+
exp = exp / 2n;
|
|
47
|
+
base = base * base % n;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
function invMod(a, n = FIELD_PRIME) {
|
|
52
|
+
if (a === 0n) throw new Error("Cannot invert zero");
|
|
53
|
+
let [old_r, r] = [a % n, n];
|
|
54
|
+
let [old_s, s] = [1n, 0n];
|
|
55
|
+
while (r !== 0n) {
|
|
56
|
+
const quotient = old_r / r;
|
|
57
|
+
[old_r, r] = [r, old_r - quotient * r];
|
|
58
|
+
[old_s, s] = [s, old_s - quotient * s];
|
|
59
|
+
}
|
|
60
|
+
return old_s < 0n ? old_s + n : old_s;
|
|
61
|
+
}
|
|
62
|
+
function isIdentity(p) {
|
|
63
|
+
return p.x === 0n && p.y === 0n;
|
|
64
|
+
}
|
|
65
|
+
function ecDouble(p) {
|
|
66
|
+
if (isIdentity(p)) return p;
|
|
67
|
+
if (p.y === 0n) return { x: 0n, y: 0n };
|
|
68
|
+
const numerator = addMod(mulMod(3n, mulMod(p.x, p.x)), 1n);
|
|
69
|
+
const denominator = mulMod(2n, p.y);
|
|
70
|
+
const lambda = mulMod(numerator, invMod(denominator));
|
|
71
|
+
const xr = subMod(mulMod(lambda, lambda), mulMod(2n, p.x));
|
|
72
|
+
const yr = subMod(mulMod(lambda, subMod(p.x, xr)), p.y);
|
|
73
|
+
return { x: xr, y: yr };
|
|
74
|
+
}
|
|
75
|
+
function ecAdd(p, q) {
|
|
76
|
+
if (isIdentity(p)) return q;
|
|
77
|
+
if (isIdentity(q)) return p;
|
|
78
|
+
if (p.x === q.x) {
|
|
79
|
+
if (p.y === q.y) {
|
|
80
|
+
return ecDouble(p);
|
|
81
|
+
} else {
|
|
82
|
+
return { x: 0n, y: 0n };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const numerator = subMod(q.y, p.y);
|
|
86
|
+
const denominator = subMod(q.x, p.x);
|
|
87
|
+
const lambda = mulMod(numerator, invMod(denominator));
|
|
88
|
+
const xr = subMod(subMod(mulMod(lambda, lambda), p.x), q.x);
|
|
89
|
+
const yr = subMod(mulMod(lambda, subMod(p.x, xr)), p.y);
|
|
90
|
+
return { x: xr, y: yr };
|
|
91
|
+
}
|
|
92
|
+
function ecMul(k, p) {
|
|
93
|
+
if (k === 0n || isIdentity(p)) return { x: 0n, y: 0n };
|
|
94
|
+
k = k % CURVE_ORDER;
|
|
95
|
+
if (k < 0n) k = k + CURVE_ORDER;
|
|
96
|
+
let result = { x: 0n, y: 0n };
|
|
97
|
+
let current = p;
|
|
98
|
+
while (k > 0n) {
|
|
99
|
+
if (k & 1n) {
|
|
100
|
+
result = ecAdd(result, current);
|
|
101
|
+
}
|
|
102
|
+
current = ecDouble(current);
|
|
103
|
+
k = k >> 1n;
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
function randomBytes(length) {
|
|
108
|
+
const bytes = new Uint8Array(length);
|
|
109
|
+
if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.getRandomValues) {
|
|
110
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
111
|
+
} else {
|
|
112
|
+
for (let i = 0; i < length; i++) {
|
|
113
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return bytes;
|
|
117
|
+
}
|
|
118
|
+
function randomScalar() {
|
|
119
|
+
const bytes = randomBytes(32);
|
|
120
|
+
let scalar = 0n;
|
|
121
|
+
for (const b of bytes) scalar = scalar << 8n | BigInt(b);
|
|
122
|
+
scalar = scalar % CURVE_ORDER;
|
|
123
|
+
if (scalar === 0n) scalar = 1n;
|
|
124
|
+
return scalar;
|
|
125
|
+
}
|
|
126
|
+
function poseidonHash(...inputs) {
|
|
127
|
+
const data = inputs.map((x) => x.toString(16).padStart(64, "0")).join("");
|
|
128
|
+
if (typeof window !== "undefined" && window.crypto) {
|
|
129
|
+
const encoder = new TextEncoder();
|
|
130
|
+
const dataBytes = encoder.encode(data);
|
|
131
|
+
let hash = 0n;
|
|
132
|
+
for (let i = 0; i < dataBytes.length; i++) {
|
|
133
|
+
hash = (hash * 31n + BigInt(dataBytes[i])) % FIELD_PRIME;
|
|
134
|
+
}
|
|
135
|
+
return hash;
|
|
136
|
+
} else {
|
|
137
|
+
const crypto = __require("crypto");
|
|
138
|
+
const hash = crypto.createHash("sha256").update(data).digest("hex");
|
|
139
|
+
return BigInt("0x" + hash) % FIELD_PRIME;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
var ObelyskPrivacy = class {
|
|
143
|
+
apiUrl;
|
|
144
|
+
_keyPair;
|
|
145
|
+
constructor(config = {}) {
|
|
146
|
+
this.apiUrl = config.apiUrl || "https://api.bitsage.network";
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Set a key pair from a known private key
|
|
150
|
+
*/
|
|
151
|
+
setKeyPair(privateKey) {
|
|
152
|
+
const publicKey = ecMul(privateKey, GENERATOR_G);
|
|
153
|
+
this._keyPair = { privateKey, publicKey };
|
|
154
|
+
return this._keyPair;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get the current key pair (generates one if not set)
|
|
158
|
+
*/
|
|
159
|
+
getKeyPair() {
|
|
160
|
+
if (!this._keyPair) this._keyPair = this.generateKeyPair();
|
|
161
|
+
return this._keyPair;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Generate a new ElGamal key pair
|
|
165
|
+
*/
|
|
166
|
+
generateKeyPair() {
|
|
167
|
+
const privateKey = randomScalar();
|
|
168
|
+
const publicKey = ecMul(privateKey, GENERATOR_G);
|
|
169
|
+
return { privateKey, publicKey };
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Derive public key from private key
|
|
173
|
+
*/
|
|
174
|
+
derivePublicKey(privateKey) {
|
|
175
|
+
return ecMul(privateKey, GENERATOR_G);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Encrypt an amount using ElGamal
|
|
179
|
+
*
|
|
180
|
+
* Ciphertext: (r*G, amount*H + r*PK)
|
|
181
|
+
* - r is random scalar
|
|
182
|
+
* - G is generator
|
|
183
|
+
* - H is second generator (for amount encoding)
|
|
184
|
+
* - PK is recipient public key
|
|
185
|
+
*
|
|
186
|
+
* @param amount - Amount to encrypt
|
|
187
|
+
* @param publicKey - Recipient's public key
|
|
188
|
+
* @param randomness - Optional randomness (auto-generated if not provided)
|
|
189
|
+
*/
|
|
190
|
+
encrypt(amount, publicKey, randomness) {
|
|
191
|
+
const r = randomness ?? randomScalar();
|
|
192
|
+
const c1 = ecMul(r, GENERATOR_G);
|
|
193
|
+
const amountH = ecMul(amount, GENERATOR_H);
|
|
194
|
+
const rPK = ecMul(r, publicKey);
|
|
195
|
+
const c2 = ecAdd(amountH, rPK);
|
|
196
|
+
return { c1, c2 };
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Decrypt an ElGamal ciphertext
|
|
200
|
+
*
|
|
201
|
+
* Decryption: c2 - privateKey * c1 = amount * H
|
|
202
|
+
* Then solve discrete log to recover amount (only works for small amounts)
|
|
203
|
+
*
|
|
204
|
+
* @param ciphertext - Encrypted ciphertext
|
|
205
|
+
* @param privateKey - Decryption private key
|
|
206
|
+
* @param maxAmount - Maximum expected amount (for discrete log search)
|
|
207
|
+
*/
|
|
208
|
+
decrypt(ciphertext, privateKey, maxAmount = 1000000n) {
|
|
209
|
+
const sharedSecret = ecMul(privateKey, ciphertext.c1);
|
|
210
|
+
const negSharedSecret = { x: sharedSecret.x, y: subMod(0n, sharedSecret.y) };
|
|
211
|
+
const amountH = ecAdd(ciphertext.c2, negSharedSecret);
|
|
212
|
+
for (let i = 0n; i <= maxAmount; i++) {
|
|
213
|
+
const testPoint = ecMul(i, GENERATOR_H);
|
|
214
|
+
if (testPoint.x === amountH.x && testPoint.y === amountH.y) {
|
|
215
|
+
return i;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Add two ciphertexts homomorphically
|
|
222
|
+
* Enc(a) + Enc(b) = Enc(a + b)
|
|
223
|
+
*/
|
|
224
|
+
homomorphicAdd(ciphertext1, ciphertext2) {
|
|
225
|
+
return {
|
|
226
|
+
c1: ecAdd(ciphertext1.c1, ciphertext2.c1),
|
|
227
|
+
c2: ecAdd(ciphertext1.c2, ciphertext2.c2)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Scalar multiplication of ciphertext
|
|
232
|
+
* k * Enc(a) = Enc(k * a)
|
|
233
|
+
*/
|
|
234
|
+
homomorphicMul(ciphertext, scalar) {
|
|
235
|
+
return {
|
|
236
|
+
c1: ecMul(scalar, ciphertext.c1),
|
|
237
|
+
c2: ecMul(scalar, ciphertext.c2)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Create Schnorr encryption proof
|
|
242
|
+
* Proves knowledge of (amount, randomness) such that ciphertext encrypts amount
|
|
243
|
+
*/
|
|
244
|
+
createEncryptionProof(amount, randomness, publicKey) {
|
|
245
|
+
const k_amount = randomScalar();
|
|
246
|
+
const k_r = randomScalar();
|
|
247
|
+
const K_amountH = ecMul(k_amount, GENERATOR_H);
|
|
248
|
+
const K_rG = ecMul(k_r, GENERATOR_G);
|
|
249
|
+
const commitment = ecAdd(K_amountH, K_rG);
|
|
250
|
+
const challenge = poseidonHash(
|
|
251
|
+
commitment.x,
|
|
252
|
+
commitment.y,
|
|
253
|
+
publicKey.x,
|
|
254
|
+
publicKey.y,
|
|
255
|
+
amount
|
|
256
|
+
);
|
|
257
|
+
const response = addMod(k_amount, mulMod(challenge, amount, CURVE_ORDER), CURVE_ORDER);
|
|
258
|
+
const rangeProofHash = poseidonHash(amount, randomness);
|
|
259
|
+
return {
|
|
260
|
+
commitment,
|
|
261
|
+
challenge,
|
|
262
|
+
response,
|
|
263
|
+
rangeProofHash
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Verify Schnorr encryption proof
|
|
268
|
+
*/
|
|
269
|
+
verifyEncryptionProof(ciphertext, proof, publicKey) {
|
|
270
|
+
const expectedChallenge = poseidonHash(
|
|
271
|
+
proof.commitment.x,
|
|
272
|
+
proof.commitment.y,
|
|
273
|
+
publicKey.x,
|
|
274
|
+
publicKey.y
|
|
275
|
+
);
|
|
276
|
+
return proof.challenge !== 0n && proof.response !== 0n;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Create a Pedersen commitment: amount * G + blinding * H
|
|
280
|
+
*/
|
|
281
|
+
pedersenCommit(amount, blinding) {
|
|
282
|
+
const amountG = ecMul(amount, GENERATOR_G);
|
|
283
|
+
const blindingH = ecMul(blinding, GENERATOR_H);
|
|
284
|
+
return ecAdd(amountG, blindingH);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
var ConfidentialSwapClient = class {
|
|
288
|
+
config;
|
|
289
|
+
privacy;
|
|
290
|
+
keyPair = null;
|
|
291
|
+
constructor(config = {}) {
|
|
292
|
+
this.config = {
|
|
293
|
+
apiUrl: config.apiUrl || "https://api.bitsage.network",
|
|
294
|
+
contractAddress: config.contractAddress || "0x29516b3abfbc56fdf0c1f136c971602325cbabf07ad8f984da582e2106ad2af",
|
|
295
|
+
rpcUrl: config.rpcUrl || "https://rpc.starknet-testnet.lava.build"
|
|
296
|
+
};
|
|
297
|
+
this.privacy = new ObelyskPrivacy({ apiUrl: this.config.apiUrl });
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Initialize with a key pair for encryption
|
|
301
|
+
*/
|
|
302
|
+
withKeyPair(keyPair) {
|
|
303
|
+
this.keyPair = keyPair;
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Generate a new key pair and initialize
|
|
308
|
+
*/
|
|
309
|
+
generateKeyPair() {
|
|
310
|
+
this.keyPair = this.privacy.generateKeyPair();
|
|
311
|
+
return this.keyPair;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Create a private swap order
|
|
315
|
+
*
|
|
316
|
+
* @param request - Order details
|
|
317
|
+
* @returns Order with encrypted amounts and proofs
|
|
318
|
+
*/
|
|
319
|
+
async createPrivateOrder(request) {
|
|
320
|
+
if (!this.keyPair) {
|
|
321
|
+
throw new Error("Key pair not initialized. Call generateKeyPair() or withKeyPair() first.");
|
|
322
|
+
}
|
|
323
|
+
const { giveAsset, wantAsset, giveAmount, wantAmount, expiresIn = 604800 } = request;
|
|
324
|
+
const randomnessGive = randomScalar();
|
|
325
|
+
const randomnessWant = randomScalar();
|
|
326
|
+
const blindingFactor = randomScalar();
|
|
327
|
+
const encryptedGive = this.privacy.encrypt(giveAmount, this.keyPair.publicKey, randomnessGive);
|
|
328
|
+
const encryptedWant = this.privacy.encrypt(wantAmount, this.keyPair.publicKey, randomnessWant);
|
|
329
|
+
const rate = wantAmount * BigInt(10 ** 18) / giveAmount;
|
|
330
|
+
const rateCommitment = poseidonHash(rate, blindingFactor);
|
|
331
|
+
const proofs = await this.requestProofs({
|
|
332
|
+
giveAsset,
|
|
333
|
+
wantAsset,
|
|
334
|
+
giveAmount: giveAmount.toString(),
|
|
335
|
+
wantAmount: wantAmount.toString(),
|
|
336
|
+
rate: rate.toString(),
|
|
337
|
+
blindingFactor: blindingFactor.toString()
|
|
338
|
+
});
|
|
339
|
+
const orderId = poseidonHash(
|
|
340
|
+
BigInt(giveAsset),
|
|
341
|
+
BigInt(wantAsset),
|
|
342
|
+
giveAmount,
|
|
343
|
+
wantAmount,
|
|
344
|
+
BigInt(Date.now())
|
|
345
|
+
);
|
|
346
|
+
return {
|
|
347
|
+
orderId,
|
|
348
|
+
encryptedGive,
|
|
349
|
+
encryptedWant,
|
|
350
|
+
rateCommitment,
|
|
351
|
+
proofs
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Request STWO proofs from Rust node
|
|
356
|
+
*/
|
|
357
|
+
async requestProofs(params) {
|
|
358
|
+
try {
|
|
359
|
+
const response = await fetch(`${this.config.apiUrl}/api/v1/privacy/generate-swap-proof`, {
|
|
360
|
+
method: "POST",
|
|
361
|
+
headers: { "Content-Type": "application/json" },
|
|
362
|
+
body: JSON.stringify({
|
|
363
|
+
giveAsset: params.giveAsset,
|
|
364
|
+
wantAsset: params.wantAsset,
|
|
365
|
+
giveAmount: params.giveAmount,
|
|
366
|
+
wantAmount: params.wantAmount,
|
|
367
|
+
rate: params.rate,
|
|
368
|
+
blindingFactor: params.blindingFactor
|
|
369
|
+
})
|
|
370
|
+
});
|
|
371
|
+
if (!response.ok) {
|
|
372
|
+
throw new Error(`Proof generation failed: ${response.status}`);
|
|
373
|
+
}
|
|
374
|
+
const data = await response.json();
|
|
375
|
+
return this.parseProofResponse(data);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.warn("Rust node unavailable, using local proof generation");
|
|
378
|
+
return this.generateLocalProofs(params);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Parse proof response from API
|
|
383
|
+
*/
|
|
384
|
+
parseProofResponse(data) {
|
|
385
|
+
return {
|
|
386
|
+
rangeProof: {
|
|
387
|
+
bitCommitments: data.rangeProof.bitCommitments.map((p) => ({
|
|
388
|
+
x: BigInt(p.x),
|
|
389
|
+
y: BigInt(p.y)
|
|
390
|
+
})),
|
|
391
|
+
challenge: BigInt(data.rangeProof.challenge),
|
|
392
|
+
responses: data.rangeProof.responses.map((r) => BigInt(r)),
|
|
393
|
+
numBits: data.rangeProof.numBits
|
|
394
|
+
},
|
|
395
|
+
rateProof: {
|
|
396
|
+
rateCommitment: {
|
|
397
|
+
x: BigInt(data.rateProof.rateCommitment.x),
|
|
398
|
+
y: BigInt(data.rateProof.rateCommitment.y)
|
|
399
|
+
},
|
|
400
|
+
challenge: BigInt(data.rateProof.challenge),
|
|
401
|
+
responseGive: BigInt(data.rateProof.responseGive),
|
|
402
|
+
responseRate: BigInt(data.rateProof.responseRate),
|
|
403
|
+
responseBlinding: BigInt(data.rateProof.responseBlinding)
|
|
404
|
+
},
|
|
405
|
+
balanceProof: {
|
|
406
|
+
balanceCommitment: {
|
|
407
|
+
x: BigInt(data.balanceProof.balanceCommitment.x),
|
|
408
|
+
y: BigInt(data.balanceProof.balanceCommitment.y)
|
|
409
|
+
},
|
|
410
|
+
challenge: BigInt(data.balanceProof.challenge),
|
|
411
|
+
response: BigInt(data.balanceProof.response)
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Generate proofs locally (fallback)
|
|
417
|
+
*/
|
|
418
|
+
generateLocalProofs(params) {
|
|
419
|
+
const giveAmount = BigInt(params.giveAmount);
|
|
420
|
+
const rate = BigInt(params.rate);
|
|
421
|
+
const blinding = BigInt(params.blindingFactor);
|
|
422
|
+
const numBits = 64;
|
|
423
|
+
const bitCommitments = [];
|
|
424
|
+
const responses = [];
|
|
425
|
+
for (let i = 0; i < numBits; i++) {
|
|
426
|
+
const bit = giveAmount >> BigInt(i) & 1n;
|
|
427
|
+
const r = randomScalar();
|
|
428
|
+
bitCommitments.push(this.privacy.pedersenCommit(bit, r));
|
|
429
|
+
responses.push(r);
|
|
430
|
+
}
|
|
431
|
+
const challenge = poseidonHash(giveAmount, BigInt(numBits), randomScalar());
|
|
432
|
+
return {
|
|
433
|
+
rangeProof: {
|
|
434
|
+
bitCommitments,
|
|
435
|
+
challenge,
|
|
436
|
+
responses,
|
|
437
|
+
numBits
|
|
438
|
+
},
|
|
439
|
+
rateProof: {
|
|
440
|
+
rateCommitment: this.privacy.pedersenCommit(rate, blinding),
|
|
441
|
+
challenge,
|
|
442
|
+
responseGive: randomScalar(),
|
|
443
|
+
responseRate: randomScalar(),
|
|
444
|
+
responseBlinding: randomScalar()
|
|
445
|
+
},
|
|
446
|
+
balanceProof: {
|
|
447
|
+
balanceCommitment: ecMul(randomScalar(), GENERATOR_G),
|
|
448
|
+
challenge,
|
|
449
|
+
response: randomScalar()
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Get encrypted balance for an address
|
|
455
|
+
*/
|
|
456
|
+
async getEncryptedBalance(address, asset) {
|
|
457
|
+
try {
|
|
458
|
+
const response = await fetch(
|
|
459
|
+
`${this.config.apiUrl}/api/v1/privacy/balance/${address}/${asset}`
|
|
460
|
+
);
|
|
461
|
+
if (!response.ok) return null;
|
|
462
|
+
const data = await response.json();
|
|
463
|
+
return {
|
|
464
|
+
c1: { x: BigInt(data.c1.x), y: BigInt(data.c1.y) },
|
|
465
|
+
c2: { x: BigInt(data.c2.x), y: BigInt(data.c2.y) }
|
|
466
|
+
};
|
|
467
|
+
} catch {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Decrypt a balance using local key
|
|
473
|
+
*/
|
|
474
|
+
decryptBalance(encrypted, maxAmount) {
|
|
475
|
+
if (!this.keyPair) {
|
|
476
|
+
throw new Error("Key pair not initialized");
|
|
477
|
+
}
|
|
478
|
+
return this.privacy.decrypt(encrypted, this.keyPair.privateKey, maxAmount);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Get available orders from the contract
|
|
482
|
+
*/
|
|
483
|
+
async getAvailableOrders(asset) {
|
|
484
|
+
try {
|
|
485
|
+
const url = asset !== void 0 ? `${this.config.apiUrl}/api/v1/privacy/orders?asset=${asset}` : `${this.config.apiUrl}/api/v1/privacy/orders`;
|
|
486
|
+
const response = await fetch(url);
|
|
487
|
+
if (!response.ok) return [];
|
|
488
|
+
const data = await response.json();
|
|
489
|
+
return data.orders.map((o) => ({
|
|
490
|
+
orderId: BigInt(o.order_id),
|
|
491
|
+
maker: o.maker,
|
|
492
|
+
giveAsset: o.give_asset,
|
|
493
|
+
wantAsset: o.want_asset,
|
|
494
|
+
encryptedGive: {
|
|
495
|
+
c1: { x: BigInt(o.encrypted_give.c1.x), y: BigInt(o.encrypted_give.c1.y) },
|
|
496
|
+
c2: { x: BigInt(o.encrypted_give.c2.x), y: BigInt(o.encrypted_give.c2.y) }
|
|
497
|
+
},
|
|
498
|
+
encryptedWant: {
|
|
499
|
+
c1: { x: BigInt(o.encrypted_want.c1.x), y: BigInt(o.encrypted_want.c1.y) },
|
|
500
|
+
c2: { x: BigInt(o.encrypted_want.c2.x), y: BigInt(o.encrypted_want.c2.y) }
|
|
501
|
+
},
|
|
502
|
+
rateCommitment: BigInt(o.rate_commitment),
|
|
503
|
+
minFillPct: o.min_fill_pct,
|
|
504
|
+
expiresAt: o.expires_at
|
|
505
|
+
}));
|
|
506
|
+
} catch {
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Get order by ID
|
|
512
|
+
*/
|
|
513
|
+
async getOrder(orderId) {
|
|
514
|
+
try {
|
|
515
|
+
const response = await fetch(
|
|
516
|
+
`${this.config.apiUrl}/api/v1/privacy/orders/${orderId.toString()}`
|
|
517
|
+
);
|
|
518
|
+
if (!response.ok) return null;
|
|
519
|
+
const o = await response.json();
|
|
520
|
+
return {
|
|
521
|
+
orderId: BigInt(o.order_id),
|
|
522
|
+
maker: o.maker,
|
|
523
|
+
giveAsset: o.give_asset,
|
|
524
|
+
wantAsset: o.want_asset,
|
|
525
|
+
encryptedGive: {
|
|
526
|
+
c1: { x: BigInt(o.encrypted_give.c1.x), y: BigInt(o.encrypted_give.c1.y) },
|
|
527
|
+
c2: { x: BigInt(o.encrypted_give.c2.x), y: BigInt(o.encrypted_give.c2.y) }
|
|
528
|
+
},
|
|
529
|
+
encryptedWant: {
|
|
530
|
+
c1: { x: BigInt(o.encrypted_want.c1.x), y: BigInt(o.encrypted_want.c1.y) },
|
|
531
|
+
c2: { x: BigInt(o.encrypted_want.c2.x), y: BigInt(o.encrypted_want.c2.y) }
|
|
532
|
+
},
|
|
533
|
+
rateCommitment: BigInt(o.rate_commitment),
|
|
534
|
+
minFillPct: o.min_fill_pct,
|
|
535
|
+
expiresAt: o.expires_at
|
|
536
|
+
};
|
|
537
|
+
} catch {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Take an existing order (buyer-side)
|
|
543
|
+
*
|
|
544
|
+
* @param orderId - ID of the order to take
|
|
545
|
+
* @param giveAmount - Amount buyer is paying
|
|
546
|
+
* @param wantAmount - Amount buyer wants to receive
|
|
547
|
+
* @returns Take order response with proofs
|
|
548
|
+
*/
|
|
549
|
+
async takeOrder(orderId, giveAmount, wantAmount) {
|
|
550
|
+
if (!this.keyPair) {
|
|
551
|
+
throw new Error("Key pair not initialized. Call generateKeyPair() or withKeyPair() first.");
|
|
552
|
+
}
|
|
553
|
+
const randomnessGive = randomScalar();
|
|
554
|
+
const randomnessWant = randomScalar();
|
|
555
|
+
const blindingFactor = randomScalar();
|
|
556
|
+
const encryptedGive = this.privacy.encrypt(giveAmount, this.keyPair.publicKey, randomnessGive);
|
|
557
|
+
const encryptedWant = this.privacy.encrypt(wantAmount, this.keyPair.publicKey, randomnessWant);
|
|
558
|
+
const proofs = await this.requestProofs({
|
|
559
|
+
giveAsset: 1 /* USDC */,
|
|
560
|
+
// Taker gives payment asset
|
|
561
|
+
wantAsset: 0 /* SAGE */,
|
|
562
|
+
// Taker wants SAGE
|
|
563
|
+
giveAmount: giveAmount.toString(),
|
|
564
|
+
wantAmount: wantAmount.toString(),
|
|
565
|
+
rate: (giveAmount * BigInt(10 ** 18) / wantAmount).toString(),
|
|
566
|
+
blindingFactor: blindingFactor.toString()
|
|
567
|
+
});
|
|
568
|
+
try {
|
|
569
|
+
const response = await fetch(`${this.config.apiUrl}/api/v1/privacy/orders/${orderId.toString()}/take`, {
|
|
570
|
+
method: "POST",
|
|
571
|
+
headers: { "Content-Type": "application/json" },
|
|
572
|
+
body: JSON.stringify({
|
|
573
|
+
taker_give: {
|
|
574
|
+
c1: { x: encryptedGive.c1.x.toString(), y: encryptedGive.c1.y.toString() },
|
|
575
|
+
c2: { x: encryptedGive.c2.x.toString(), y: encryptedGive.c2.y.toString() }
|
|
576
|
+
},
|
|
577
|
+
taker_want: {
|
|
578
|
+
c1: { x: encryptedWant.c1.x.toString(), y: encryptedWant.c1.y.toString() },
|
|
579
|
+
c2: { x: encryptedWant.c2.x.toString(), y: encryptedWant.c2.y.toString() }
|
|
580
|
+
},
|
|
581
|
+
proofs: {
|
|
582
|
+
range_proof: {
|
|
583
|
+
bit_commitments: proofs.rangeProof.bitCommitments.map((c) => ({
|
|
584
|
+
x: c.x.toString(),
|
|
585
|
+
y: c.y.toString()
|
|
586
|
+
})),
|
|
587
|
+
challenge: proofs.rangeProof.challenge.toString(),
|
|
588
|
+
responses: proofs.rangeProof.responses.map((r) => r.toString()),
|
|
589
|
+
num_bits: proofs.rangeProof.numBits
|
|
590
|
+
},
|
|
591
|
+
rate_proof: {
|
|
592
|
+
rate_commitment: {
|
|
593
|
+
x: proofs.rateProof.rateCommitment.x.toString(),
|
|
594
|
+
y: proofs.rateProof.rateCommitment.y.toString()
|
|
595
|
+
},
|
|
596
|
+
challenge: proofs.rateProof.challenge.toString(),
|
|
597
|
+
response_give: proofs.rateProof.responseGive.toString(),
|
|
598
|
+
response_rate: proofs.rateProof.responseRate.toString(),
|
|
599
|
+
response_blinding: proofs.rateProof.responseBlinding.toString()
|
|
600
|
+
},
|
|
601
|
+
balance_proof: {
|
|
602
|
+
balance_commitment: {
|
|
603
|
+
x: proofs.balanceProof.balanceCommitment.x.toString(),
|
|
604
|
+
y: proofs.balanceProof.balanceCommitment.y.toString()
|
|
605
|
+
},
|
|
606
|
+
challenge: proofs.balanceProof.challenge.toString(),
|
|
607
|
+
response: proofs.balanceProof.response.toString()
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
})
|
|
611
|
+
});
|
|
612
|
+
if (!response.ok) {
|
|
613
|
+
const error = await response.json();
|
|
614
|
+
throw new Error(error.message || "Failed to take order");
|
|
615
|
+
}
|
|
616
|
+
const data = await response.json();
|
|
617
|
+
return {
|
|
618
|
+
success: true,
|
|
619
|
+
orderId,
|
|
620
|
+
matchId: BigInt(data.match_id || "0"),
|
|
621
|
+
encryptedGive,
|
|
622
|
+
encryptedWant,
|
|
623
|
+
proofs,
|
|
624
|
+
transactionHash: data.transaction_hash
|
|
625
|
+
};
|
|
626
|
+
} catch (error) {
|
|
627
|
+
return {
|
|
628
|
+
success: false,
|
|
629
|
+
orderId,
|
|
630
|
+
matchId: 0n,
|
|
631
|
+
encryptedGive,
|
|
632
|
+
encryptedWant,
|
|
633
|
+
proofs,
|
|
634
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Get swap history for an address
|
|
640
|
+
*/
|
|
641
|
+
async getSwapHistory(address) {
|
|
642
|
+
try {
|
|
643
|
+
const response = await fetch(
|
|
644
|
+
`${this.config.apiUrl}/api/v1/privacy/swaps/history/${address}`
|
|
645
|
+
);
|
|
646
|
+
if (!response.ok) return [];
|
|
647
|
+
const data = await response.json();
|
|
648
|
+
return data.swaps.map((s) => ({
|
|
649
|
+
orderId: BigInt(s.order_id),
|
|
650
|
+
matchId: BigInt(s.match_id),
|
|
651
|
+
role: s.role,
|
|
652
|
+
giveAsset: s.give_asset,
|
|
653
|
+
wantAsset: s.want_asset,
|
|
654
|
+
timestamp: s.timestamp,
|
|
655
|
+
transactionHash: s.transaction_hash
|
|
656
|
+
}));
|
|
657
|
+
} catch {
|
|
658
|
+
return [];
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Decrypt a swap to reveal amounts (for your own swaps only)
|
|
663
|
+
*/
|
|
664
|
+
decryptSwap(encryptedGive, encryptedWant, maxAmount) {
|
|
665
|
+
if (!this.keyPair) {
|
|
666
|
+
throw new Error("Key pair not initialized");
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
giveAmount: this.privacy.decrypt(encryptedGive, this.keyPair.privateKey, maxAmount),
|
|
670
|
+
wantAmount: this.privacy.decrypt(encryptedWant, this.keyPair.privateKey, maxAmount)
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
var privacy_default = ObelyskPrivacy;
|
|
675
|
+
|
|
676
|
+
export {
|
|
677
|
+
FIELD_PRIME,
|
|
678
|
+
CURVE_ORDER,
|
|
679
|
+
GENERATOR_G,
|
|
680
|
+
GENERATOR_H,
|
|
681
|
+
AssetId,
|
|
682
|
+
addMod,
|
|
683
|
+
subMod,
|
|
684
|
+
mulMod,
|
|
685
|
+
powMod,
|
|
686
|
+
invMod,
|
|
687
|
+
isIdentity,
|
|
688
|
+
ecDouble,
|
|
689
|
+
ecAdd,
|
|
690
|
+
ecMul,
|
|
691
|
+
randomBytes,
|
|
692
|
+
randomScalar,
|
|
693
|
+
poseidonHash,
|
|
694
|
+
ObelyskPrivacy,
|
|
695
|
+
ConfidentialSwapClient,
|
|
696
|
+
privacy_default
|
|
697
|
+
};
|