@obelyzk/sdk 1.0.0 → 1.1.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 +370 -346
- package/bin/bitsage-demo.ts +0 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +16 -6
- package/dist/index.mjs +17 -7
- package/dist/obelysk/index.d.mts +96 -7
- package/dist/obelysk/index.d.ts +96 -7
- package/dist/obelysk/index.js +325 -57
- package/dist/obelysk/index.mjs +313 -55
- package/dist/privacy/index.mjs +1 -1
- package/dist/react/index.mjs +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,478 +1,502 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @obelyzk/sdk
|
|
2
2
|
|
|
3
|
-
Official TypeScript SDK for the
|
|
3
|
+
Official TypeScript SDK for the **Obelysk Protocol** -- privacy-first DeFi on Starknet.
|
|
4
|
+
|
|
5
|
+
All cryptography runs client-side. The SDK provides ~150 methods across 10 protocol sub-clients, covering privacy pools, dark pool auctions, stealth payments, confidential transfers, shielded swaps, UTXO vaults, OTC trading, and staking.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
npm install @
|
|
9
|
-
# or
|
|
10
|
-
yarn add @bitsage/sdk
|
|
11
|
-
# or
|
|
12
|
-
pnpm add @bitsage/sdk
|
|
10
|
+
npm install @obelyzk/sdk starknet
|
|
13
11
|
```
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
`starknet` (v6+) is a required peer dependency. For React hooks, also ensure `react >= 18`.
|
|
14
|
+
|
|
15
|
+
## Entry Points
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
| Import path | Description |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `@obelyzk/sdk` | Main SDK (distributed compute, STWO prover, ZKML) |
|
|
20
|
+
| `@obelyzk/sdk/obelysk` | Obelysk Protocol client (privacy DeFi -- this document) |
|
|
21
|
+
| `@obelyzk/sdk/privacy` | ElGamal primitives, EC math, Schnorr proofs |
|
|
22
|
+
| `@obelyzk/sdk/react` | React hooks for wallet, privacy, and prover state |
|
|
22
23
|
|
|
23
24
|
---
|
|
24
25
|
|
|
25
|
-
## Quick Start
|
|
26
|
+
## Quick Start (Read-Only)
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
No wallet or account needed for read-only queries:
|
|
28
29
|
|
|
29
30
|
```typescript
|
|
30
|
-
import {
|
|
31
|
+
import { ObelyskClient } from '@obelyzk/sdk/obelysk';
|
|
31
32
|
|
|
32
|
-
const
|
|
33
|
-
network: '
|
|
34
|
-
|
|
33
|
+
const obelysk = new ObelyskClient({
|
|
34
|
+
network: 'mainnet',
|
|
35
|
+
rpcUrl: 'https://your-rpc.com',
|
|
35
36
|
});
|
|
36
37
|
|
|
37
|
-
//
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
input_data: btoa('Hello, BitSage!'),
|
|
41
|
-
max_cost_sage: 100n,
|
|
42
|
-
priority: 5,
|
|
43
|
-
});
|
|
38
|
+
// Query DarkPool epoch
|
|
39
|
+
const epoch = await obelysk.darkPool.getEpochInfo();
|
|
40
|
+
console.log('Epoch:', epoch.epoch, 'Phase:', epoch.phase);
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
// Query privacy pool stats
|
|
43
|
+
const root = await obelysk.privacyPool.getMerkleRoot('eth');
|
|
44
|
+
console.log('ETH pool Merkle root:', root);
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
// Query staking
|
|
47
|
+
const totalStaked = await obelysk.staking.getTotalStaked();
|
|
48
|
+
console.log('Total SAGE staked:', totalStaked);
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
// Query OTC orderbook depth
|
|
51
|
+
const depth = await obelysk.otc.getOrderbookDepth(0);
|
|
52
|
+
console.log('Bids:', depth.bids.length, 'Asks:', depth.asks.length);
|
|
53
|
+
```
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
---
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
## ObelyskClient Configuration
|
|
55
58
|
|
|
56
59
|
```typescript
|
|
57
|
-
import {
|
|
60
|
+
import { ObelyskClient } from '@obelyzk/sdk/obelysk';
|
|
61
|
+
import { Account, RpcProvider } from 'starknet';
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
// Generate your key pair (store securely!)
|
|
63
|
-
const keyPair = privacy.generateKeyPair();
|
|
64
|
-
console.log('Public Key:', keyPair.publicKey);
|
|
63
|
+
const provider = new RpcProvider({ nodeUrl: 'https://your-rpc.com' });
|
|
64
|
+
const account = new Account(provider, '0xADDRESS', '0xPRIVATE_KEY');
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
console.log('Encrypted:', encrypted.c1, encrypted.c2);
|
|
66
|
+
const obelysk = new ObelyskClient({
|
|
67
|
+
// Required
|
|
68
|
+
network: 'mainnet', // 'mainnet' | 'sepolia'
|
|
70
69
|
|
|
71
|
-
//
|
|
72
|
-
|
|
70
|
+
// Recommended
|
|
71
|
+
rpcUrl: 'https://your-rpc.com', // default public RPCs have rate limits
|
|
73
72
|
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
console.log('Proof valid:', isValid); // true
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Confidential Swaps
|
|
73
|
+
// Required for write operations
|
|
74
|
+
account, // starknet.js Account
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
// Optional: VM31 relayer (for UTXO vault operations)
|
|
77
|
+
relayerUrl: 'https://relay.bitsage.network:3080',
|
|
78
|
+
relayerApiKey: 'your-api-key',
|
|
83
79
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
proverUrl: 'https://prover.bitsage.network',
|
|
80
|
+
// Optional: ElGamal private key (hex). If omitted, generated fresh.
|
|
81
|
+
privacyPrivateKey: '0x...',
|
|
87
82
|
});
|
|
83
|
+
```
|
|
88
84
|
|
|
89
|
-
|
|
90
|
-
const order = await swapClient.createPrivateOrder({
|
|
91
|
-
giveAsset: 'SAGE',
|
|
92
|
-
wantAsset: 'USDC',
|
|
93
|
-
giveAmount: 1000n,
|
|
94
|
-
wantAmount: 100n,
|
|
95
|
-
});
|
|
85
|
+
All 14 mainnet contract addresses are baked into the SDK. No manual address configuration is needed.
|
|
96
86
|
|
|
97
|
-
|
|
98
|
-
console.log('Encrypted amounts:', order.encryptedGive, order.encryptedWant);
|
|
99
|
-
```
|
|
87
|
+
---
|
|
100
88
|
|
|
101
|
-
|
|
89
|
+
## Sub-Clients
|
|
102
90
|
|
|
103
|
-
|
|
104
|
-
|-----------|---------|-------|------|
|
|
105
|
-
| `encrypt()` | Full | < 1ms | Free |
|
|
106
|
-
| `createEncryptionProof()` | Full | ~50ms | Free |
|
|
107
|
-
| `createRangeProof()` | Full | ~100ms | Free |
|
|
108
|
-
| `verifyProof()` | N/A | < 10ms | Free |
|
|
109
|
-
| On-chain verification | Full | N/A | ~$0.03 |
|
|
91
|
+
The `ObelyskClient` exposes 10 sub-clients, each handling a specific protocol domain.
|
|
110
92
|
|
|
111
|
-
|
|
93
|
+
### 1. Privacy Pool (`obelysk.privacyPool`)
|
|
112
94
|
|
|
113
|
-
|
|
95
|
+
Deposit tokens into shielded pools using Pedersen commitments. Withdraw with nullifier + Merkle proof to break the on-chain link between depositor and recipient.
|
|
114
96
|
|
|
115
|
-
|
|
97
|
+
```typescript
|
|
98
|
+
// Deposit 0.1 ETH into the privacy pool
|
|
99
|
+
const { txHash, note, commitmentHash } = await obelysk.privacyPool.deposit({
|
|
100
|
+
token: 'eth',
|
|
101
|
+
amount: '0.1',
|
|
102
|
+
});
|
|
103
|
+
// IMPORTANT: Save `note` securely -- it is required for withdrawal.
|
|
104
|
+
|
|
105
|
+
// Withdraw to a different address
|
|
106
|
+
const result = await obelysk.privacyPool.withdraw({
|
|
107
|
+
note,
|
|
108
|
+
recipient: '0xRecipient...',
|
|
109
|
+
merkleProof: ['0x...', '0x...'],
|
|
110
|
+
leafIndex: 42,
|
|
111
|
+
});
|
|
116
112
|
|
|
117
|
-
|
|
113
|
+
// Read-only queries
|
|
114
|
+
const root = await obelysk.privacyPool.getMerkleRoot('eth');
|
|
115
|
+
const stats = await obelysk.privacyPool.getPoolStats('eth');
|
|
116
|
+
```
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|----------|---------------|---------|
|
|
121
|
-
| Single payment | Client-side Schnorr | - |
|
|
122
|
-
| 100+ payments | STWO GPU batch | 95% |
|
|
123
|
-
| AI/ML inference | STWO GPU | Required |
|
|
124
|
-
| Cross-chain bridge | STWO GPU | 98% |
|
|
125
|
-
| Gaming state | STWO GPU batch | 99% |
|
|
118
|
+
### 2. DarkPool (`obelysk.darkPool`)
|
|
126
119
|
|
|
127
|
-
|
|
120
|
+
Commit-reveal batch auction trading. Orders are committed as hashes during the commit phase, revealed during the reveal phase, and matched during settlement.
|
|
128
121
|
|
|
129
122
|
```typescript
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
123
|
+
// Fund your DarkPool balance
|
|
124
|
+
await obelysk.darkPool.deposit({ token: 'sage', amount: '10000' });
|
|
125
|
+
|
|
126
|
+
// Commit an order (commit phase)
|
|
127
|
+
const { orderData } = await obelysk.darkPool.commitOrder({
|
|
128
|
+
pair: 'SAGE/STRK',
|
|
129
|
+
side: 'sell',
|
|
130
|
+
price: '0.011',
|
|
131
|
+
amount: '5000',
|
|
135
132
|
});
|
|
133
|
+
// Save `orderData` -- needed for reveal
|
|
136
134
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
// Reveal the order (reveal phase)
|
|
136
|
+
await obelysk.darkPool.revealOrder({ orderData });
|
|
137
|
+
|
|
138
|
+
// Claim fills after settlement
|
|
139
|
+
await obelysk.darkPool.claimFills({ epoch: orderData.epoch });
|
|
140
|
+
|
|
141
|
+
// Read-only
|
|
142
|
+
const epoch = await obelysk.darkPool.getEpochInfo();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 3. Stealth Payments (`obelysk.stealth`)
|
|
144
146
|
|
|
145
|
-
|
|
146
|
-
console.log('Estimated cost:', job.estimatedCostUsdc, 'USDC');
|
|
147
|
-
console.log('Estimated time:', job.estimatedTimeSecs, 'seconds');
|
|
147
|
+
Send tokens to one-time stealth addresses. The sender derives an ephemeral address from the receiver's stealth meta-address. Only the receiver can detect and claim the payment.
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
```typescript
|
|
150
|
+
// Send 1000 SAGE to a stealth address
|
|
151
|
+
const { txHash, announcement } = await obelysk.stealth.send({
|
|
152
|
+
to: '0xReceiverAddress...',
|
|
153
|
+
token: 'sage',
|
|
154
|
+
amount: '1000',
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
// Receiver: scan announcements and claim
|
|
158
|
+
const announcements = await obelysk.stealth.getAnnouncements(fromBlock);
|
|
159
|
+
const claimable = await obelysk.stealth.scanForPayments(announcements, viewKey);
|
|
160
|
+
await obelysk.stealth.claim({ announcementIndex: 5, spendingProof, recipient: '0x...' });
|
|
159
161
|
```
|
|
160
162
|
|
|
161
|
-
###
|
|
163
|
+
### 4. Confidential Transfer (`obelysk.confidentialTransfer`)
|
|
164
|
+
|
|
165
|
+
Peer-to-peer transfers where the amount is encrypted on-chain using ElGamal. Only sender and recipient can see the value.
|
|
162
166
|
|
|
163
167
|
```typescript
|
|
164
|
-
//
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const result = await prover.proveBatchPayments(payments, {
|
|
171
|
-
priority: 'high',
|
|
172
|
-
requireTee: true, // Use TEE for privacy
|
|
168
|
+
// Transfer 500 SAGE with encrypted amount
|
|
169
|
+
const { txHash } = await obelysk.confidentialTransfer.transfer({
|
|
170
|
+
to: '0xRecipient...',
|
|
171
|
+
recipientPublicKey: { x: 0x...n, y: 0x...n },
|
|
172
|
+
amount: '500',
|
|
173
|
+
token: 'sage',
|
|
173
174
|
});
|
|
174
175
|
|
|
175
|
-
//
|
|
176
|
-
|
|
176
|
+
// Query encrypted balance
|
|
177
|
+
const encBalance = await obelysk.confidentialTransfer.getEncryptedBalance('0xAddress...');
|
|
178
|
+
|
|
179
|
+
// Decrypt locally (requires private key)
|
|
180
|
+
const balance = obelysk.privacy.decrypt(encBalance, privateKey);
|
|
177
181
|
```
|
|
178
182
|
|
|
179
|
-
###
|
|
183
|
+
### 5. Prover Staking (`obelysk.staking`)
|
|
184
|
+
|
|
185
|
+
Stake SAGE tokens to participate as a GPU prover in the network. Four tiers: Consumer, Professional, DataCenter, H100.
|
|
180
186
|
|
|
181
187
|
```typescript
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
'llama-7b', // Model ID
|
|
185
|
-
[/* input tokens */], // Inputs
|
|
186
|
-
[/* output tokens */], // Outputs
|
|
187
|
-
{ requireTee: true } // Keep data private
|
|
188
|
-
);
|
|
188
|
+
// Stake 50,000 SAGE as a DataCenter prover
|
|
189
|
+
await obelysk.staking.stake({ amount: '50000', gpuTier: 'DataCenter' });
|
|
189
190
|
|
|
190
|
-
|
|
191
|
+
// Query staking info
|
|
192
|
+
const config = await obelysk.staking.getConfig();
|
|
193
|
+
const worker = await obelysk.staking.getWorkerStake('0xWorker...');
|
|
194
|
+
const total = await obelysk.staking.getTotalStaked();
|
|
195
|
+
|
|
196
|
+
// Unstake (after cooldown)
|
|
197
|
+
await obelysk.staking.unstake();
|
|
191
198
|
```
|
|
192
199
|
|
|
193
|
-
###
|
|
200
|
+
### 6. Privacy Router (`obelysk.router`)
|
|
201
|
+
|
|
202
|
+
Read-only views into the Privacy Router contract -- private account registration, encrypted balances, nullifier tracking, and audit support.
|
|
194
203
|
|
|
195
204
|
```typescript
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
'
|
|
199
|
-
'
|
|
200
|
-
|
|
201
|
-
{ priority: 'critical' }
|
|
202
|
-
);
|
|
205
|
+
const account = await obelysk.router.getAccount('0xAddress...');
|
|
206
|
+
if (account?.registered) {
|
|
207
|
+
console.log('Public key:', account.publicKey);
|
|
208
|
+
console.log('Encrypted balance:', account.encryptedBalance);
|
|
209
|
+
}
|
|
203
210
|
|
|
204
|
-
|
|
211
|
+
const spent = await obelysk.router.isNullifierUsed('0xNullifier...');
|
|
205
212
|
```
|
|
206
213
|
|
|
207
|
-
###
|
|
214
|
+
### 7. Shielded Swap (`obelysk.swap`)
|
|
215
|
+
|
|
216
|
+
Privacy-preserving AMM swaps routed through Ekubo. Swaps consume a privacy pool withdrawal proof and produce a new shielded commitment.
|
|
208
217
|
|
|
209
218
|
```typescript
|
|
210
|
-
//
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
'0x333...',
|
|
215
|
-
];
|
|
216
|
-
|
|
217
|
-
const aggregated = await prover.aggregateProofs(proofHashes);
|
|
218
|
-
console.log('Aggregated proof:', aggregated.proofHash);
|
|
219
|
-
// Verify once instead of 3 times!
|
|
219
|
+
// Query swap router state
|
|
220
|
+
const swapCount = await obelysk.swap.getSwapCount();
|
|
221
|
+
const pool = await obelysk.swap.getPool(tokenAddress);
|
|
222
|
+
const fee = await obelysk.swap.getFee();
|
|
220
223
|
```
|
|
221
224
|
|
|
222
|
-
###
|
|
225
|
+
### 8. VM31 UTXO Vault (`obelysk.vm31`)
|
|
226
|
+
|
|
227
|
+
UTXO-based privacy vault using Poseidon2-M31 hashing. Transactions are submitted to the VM31 relayer, which batches and proves them on-chain.
|
|
223
228
|
|
|
224
229
|
```typescript
|
|
225
|
-
//
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
230
|
+
// Deposit 0.01 BTC (denomination-safe)
|
|
231
|
+
const result = await obelysk.vm31.deposit({
|
|
232
|
+
amount: 1_000_000n, // 0.01 BTC in satoshis
|
|
233
|
+
assetId: 0, // wBTC
|
|
234
|
+
recipientPubkey: [/* M31 field elements */],
|
|
235
|
+
recipientViewingKey: [/* M31 field elements */],
|
|
231
236
|
});
|
|
232
237
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
console.log(' Base:', estimate.breakdown.baseCost);
|
|
236
|
-
console.log(' Priority:', estimate.breakdown.prioritySurcharge);
|
|
237
|
-
console.log(' TEE:', estimate.breakdown.teeSurcharge);
|
|
238
|
-
```
|
|
238
|
+
// Check batch status
|
|
239
|
+
const batch = await obelysk.vm31.getBatchStatus(result.batchId);
|
|
239
240
|
|
|
240
|
-
|
|
241
|
+
// Query on-chain state
|
|
242
|
+
const root = await obelysk.vm31.getMerkleRoot();
|
|
243
|
+
const noteCount = await obelysk.vm31.getNoteCount();
|
|
244
|
+
const relayer = await obelysk.vm31.getRelayerInfo();
|
|
245
|
+
```
|
|
241
246
|
|
|
242
|
-
|
|
243
|
-
// Check network status
|
|
244
|
-
const metrics = await prover.getMetrics();
|
|
247
|
+
### 9. VM31 Bridge (`obelysk.bridge`)
|
|
245
248
|
|
|
246
|
-
|
|
247
|
-
for (const gpu of metrics.availableGpus) {
|
|
248
|
-
console.log(` ${gpu.tier}: ${gpu.count} (${gpu.teeEnabled} TEE)`);
|
|
249
|
-
}
|
|
249
|
+
Bridge between the VM31 UTXO pool and ElGamal-based ConfidentialTransfer system.
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
251
|
+
```typescript
|
|
252
|
+
const config = await obelysk.bridge.getConfig();
|
|
253
|
+
const assetPair = await obelysk.bridge.getAssetPair(0); // wBTC mapping
|
|
254
|
+
const relayer = await obelysk.bridge.getRelayer();
|
|
254
255
|
```
|
|
255
256
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
## Full Privacy Client
|
|
257
|
+
### 10. OTC Orderbook (`obelysk.otc`)
|
|
259
258
|
|
|
260
|
-
|
|
259
|
+
On-chain limit and market order book for peer-to-peer token trading.
|
|
261
260
|
|
|
262
261
|
```typescript
|
|
263
|
-
import {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
262
|
+
import { OrderSide, OrderType } from '@obelyzk/sdk/obelysk';
|
|
263
|
+
|
|
264
|
+
// Place a limit order
|
|
265
|
+
const orderId = await obelysk.otc.placeLimitOrder({
|
|
266
|
+
pairId: 0,
|
|
267
|
+
side: OrderSide.Buy,
|
|
268
|
+
price: 11_000_000_000_000_000n, // 0.011 in 18-decimal
|
|
269
|
+
amount: 5000_000_000_000_000_000_000n,
|
|
268
270
|
});
|
|
269
271
|
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
recipientPublicKey: recipient.publicKey,
|
|
276
|
-
amount: 500n,
|
|
277
|
-
asset: 'SAGE',
|
|
272
|
+
// Place a market order
|
|
273
|
+
await obelysk.otc.placeMarketOrder({
|
|
274
|
+
pairId: 0,
|
|
275
|
+
side: OrderSide.Sell,
|
|
276
|
+
amount: 1000_000_000_000_000_000_000n,
|
|
278
277
|
});
|
|
279
278
|
|
|
280
|
-
//
|
|
281
|
-
const
|
|
279
|
+
// Query orderbook
|
|
280
|
+
const depth = await obelysk.otc.getOrderbookDepth(0);
|
|
281
|
+
const stats = await obelysk.otc.get24hStats(0);
|
|
282
|
+
const order = await obelysk.otc.getOrder(orderId);
|
|
283
|
+
const trades = await obelysk.otc.getRecentTrades(0);
|
|
282
284
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
console.log('My SAGE balance:', revealed);
|
|
285
|
+
// Cancel
|
|
286
|
+
await obelysk.otc.cancelOrder(orderId);
|
|
286
287
|
```
|
|
287
288
|
|
|
288
289
|
---
|
|
289
290
|
|
|
290
|
-
##
|
|
291
|
+
## ElGamal Privacy Primitives
|
|
292
|
+
|
|
293
|
+
Available from both `@obelyzk/sdk/privacy` and `@obelyzk/sdk/obelysk`.
|
|
291
294
|
|
|
292
295
|
```typescript
|
|
293
|
-
import {
|
|
296
|
+
import { ObelyskPrivacy } from '@obelyzk/sdk/privacy';
|
|
294
297
|
|
|
295
|
-
|
|
296
|
-
const { client, isConnected } = useBitSage();
|
|
297
|
-
const { privacy, keyPair, generateKeyPair } = usePrivacy();
|
|
298
|
-
const { prover, submitJob, status } = useStwoProver();
|
|
298
|
+
const privacy = new ObelyskPrivacy();
|
|
299
299
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
300
|
+
// Generate a key pair
|
|
301
|
+
const keyPair = privacy.generateKeyPair();
|
|
302
|
+
console.log('Public key:', keyPair.publicKey); // ECPoint { x, y }
|
|
303
|
+
|
|
304
|
+
// Encrypt an amount (only the private key holder can decrypt)
|
|
305
|
+
const ciphertext = privacy.encrypt(1_000_000n, keyPair.publicKey);
|
|
306
|
+
// ciphertext = { c1: ECPoint, c2: ECPoint }
|
|
307
|
+
|
|
308
|
+
// Decrypt
|
|
309
|
+
const amount = privacy.decrypt(ciphertext, keyPair.privateKey);
|
|
310
|
+
console.log(amount); // 1000000n
|
|
311
|
+
|
|
312
|
+
// Homomorphic addition (add encrypted values without decrypting)
|
|
313
|
+
const ct1 = privacy.encrypt(100n, keyPair.publicKey);
|
|
314
|
+
const ct2 = privacy.encrypt(200n, keyPair.publicKey);
|
|
315
|
+
const ctSum = privacy.homomorphicAdd(ct1, ct2);
|
|
316
|
+
const sum = privacy.decrypt(ctSum, keyPair.privateKey);
|
|
317
|
+
console.log(sum); // 300n
|
|
318
|
+
|
|
319
|
+
// Schnorr proof of correct encryption
|
|
320
|
+
const proof = privacy.createEncryptionProof(1_000_000n, keyPair, ciphertext);
|
|
321
|
+
const valid = privacy.verifyEncryptionProof(proof, keyPair.publicKey, ciphertext);
|
|
308
322
|
```
|
|
309
323
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
## Contract Registry
|
|
324
|
+
### Low-Level Crypto Functions
|
|
313
325
|
|
|
314
326
|
```typescript
|
|
315
327
|
import {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
+
elgamalEncrypt,
|
|
329
|
+
pedersenCommit,
|
|
330
|
+
createEncryptionProof,
|
|
331
|
+
createAEHint,
|
|
332
|
+
commitmentToHash,
|
|
333
|
+
deriveNullifier,
|
|
334
|
+
randomScalar,
|
|
335
|
+
ecAdd,
|
|
336
|
+
ecMul,
|
|
337
|
+
mod,
|
|
338
|
+
modInverse,
|
|
339
|
+
FIELD_PRIME,
|
|
340
|
+
CURVE_ORDER,
|
|
341
|
+
GENERATOR_G,
|
|
342
|
+
GENERATOR_H,
|
|
343
|
+
} from '@obelyzk/sdk/obelysk';
|
|
344
|
+
|
|
345
|
+
// Pedersen commitment: C = value * G + blinding * H
|
|
346
|
+
const blinding = randomScalar();
|
|
347
|
+
const commitment = pedersenCommit(1000n, blinding);
|
|
348
|
+
|
|
349
|
+
// ElGamal encrypt: (r*G, amount*H + r*PK)
|
|
350
|
+
const { c1, c2, randomness } = elgamalEncrypt(500n, publicKey);
|
|
351
|
+
|
|
352
|
+
// Schnorr proof of knowledge of r
|
|
353
|
+
const proof = createEncryptionProof(randomness, c1);
|
|
354
|
+
|
|
355
|
+
// AE hint for O(1) decryption
|
|
356
|
+
const { encryptedAmount, mac } = createAEHint(500n, sharedSecret);
|
|
357
|
+
|
|
358
|
+
// Nullifier derivation (Poseidon hash)
|
|
359
|
+
const nullifier = deriveNullifier(nullifierSecret, commitmentToHash(commitment));
|
|
328
360
|
```
|
|
329
361
|
|
|
330
362
|
---
|
|
331
363
|
|
|
332
|
-
##
|
|
364
|
+
## ECIES Encryption
|
|
333
365
|
|
|
366
|
+
ECIES (X25519 + HKDF-SHA256 + AES-256-GCM) is used to encrypt transaction payloads for the VM31 relayer. Compatible with the Rust relayer (`x25519-dalek` + `aes-gcm` + `hkdf`).
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import { eciesEncrypt } from '@obelyzk/sdk/obelysk';
|
|
370
|
+
import type { ECIESEnvelope } from '@obelyzk/sdk/obelysk';
|
|
371
|
+
|
|
372
|
+
const envelope: ECIESEnvelope = await eciesEncrypt(
|
|
373
|
+
{ action: 'deposit', amount: '1000000', assetId: 0 },
|
|
374
|
+
relayerPublicKeyHex, // 64-char hex X25519 public key
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
// POST the envelope to the relayer
|
|
378
|
+
const response = await fetch('https://relay.bitsage.network:3080/submit', {
|
|
379
|
+
method: 'POST',
|
|
380
|
+
headers: { 'Content-Type': 'application/json' },
|
|
381
|
+
body: JSON.stringify(envelope),
|
|
382
|
+
});
|
|
334
383
|
```
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
├─────────────────────────────────────────────────────────────────────────────┤
|
|
338
|
-
│ │
|
|
339
|
-
│ OBELYSK PRIVACY (Client-Side) STWO GPU PROVER (Server-Side) │
|
|
340
|
-
│ ───────────────────────────── ────────────────────────────── │
|
|
341
|
-
│ • ElGamal encryption • Batch proof generation │
|
|
342
|
-
│ • Schnorr proofs • GPU acceleration (H100/H200) │
|
|
343
|
-
│ • Range proofs • TEE privacy (optional) │
|
|
344
|
-
│ • Confidential swaps • Recursive aggregation │
|
|
345
|
-
│ │
|
|
346
|
-
│ import { ObelyskPrivacy } import { createStwoProverClient } │
|
|
347
|
-
│ │
|
|
348
|
-
├─────────────────────────────────────────────────────────────────────────────┤
|
|
349
|
-
│ │
|
|
350
|
-
│ STARKNET L2 │
|
|
351
|
-
│ • Proof verification │
|
|
352
|
-
│ • Encrypted balances │
|
|
353
|
-
│ • Atomic swaps │
|
|
354
|
-
│ │
|
|
355
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
356
|
-
```
|
|
384
|
+
|
|
385
|
+
Requires Web Crypto API (Node 20+ or any modern browser).
|
|
357
386
|
|
|
358
387
|
---
|
|
359
388
|
|
|
360
|
-
##
|
|
361
|
-
|
|
362
|
-
### ObelyskPrivacy
|
|
363
|
-
|
|
364
|
-
| Method | Description |
|
|
365
|
-
|--------|-------------|
|
|
366
|
-
| `generateKeyPair()` | Generate ElGamal key pair |
|
|
367
|
-
| `encrypt(amount, publicKey)` | Encrypt amount |
|
|
368
|
-
| `decrypt(ciphertext, privateKey)` | Decrypt ciphertext |
|
|
369
|
-
| `homomorphicAdd(c1, c2)` | Add encrypted values |
|
|
370
|
-
| `createEncryptionProof()` | Create Schnorr proof |
|
|
371
|
-
| `verifyEncryptionProof()` | Verify proof |
|
|
372
|
-
|
|
373
|
-
### StwoProverClient
|
|
374
|
-
|
|
375
|
-
| Method | Description |
|
|
376
|
-
|--------|-------------|
|
|
377
|
-
| `submitProofJob(request)` | Submit proof generation job |
|
|
378
|
-
| `getJobStatus(jobId)` | Get job status |
|
|
379
|
-
| `waitForProof(jobId)` | Wait for completion |
|
|
380
|
-
| `cancelJob(jobId)` | Cancel pending job |
|
|
381
|
-
| `submitBatch(request)` | Submit batch of proofs |
|
|
382
|
-
| `estimateCost(request)` | Estimate proof cost |
|
|
383
|
-
| `getMetrics()` | Get network metrics |
|
|
384
|
-
| `proveBatchPayments()` | Helper for batch payments |
|
|
385
|
-
| `proveInference()` | Helper for AI inference |
|
|
386
|
-
| `proveBridge()` | Helper for bridge proofs |
|
|
387
|
-
| `aggregateProofs()` | Recursive aggregation |
|
|
388
|
-
| `loadZkmlModel(req)` | Load ONNX model on prover server |
|
|
389
|
-
| `submitZkmlProve(req)` | Submit ZKML proving job |
|
|
390
|
-
| `getZkmlProveStatus(jobId)` | Get ZKML job progress |
|
|
391
|
-
| `getZkmlProveResult(jobId)` | Get proof calldata + commitments |
|
|
392
|
-
| `proveZkml(req, opts?)` | Full prove pipeline with progress callback |
|
|
393
|
-
|
|
394
|
-
### StwoClient (On-Chain Verification)
|
|
395
|
-
|
|
396
|
-
| Method | Description |
|
|
397
|
-
|--------|-------------|
|
|
398
|
-
| `submitProof(data, hash)` | Submit proof for verification |
|
|
399
|
-
| `verifyProof(hash)` | Verify proof via contract |
|
|
400
|
-
| `submitGpuTeeProof(params)` | Submit GPU-TEE optimistic proof |
|
|
401
|
-
| `registerZkmlModel(id, commitment)` | Register model on verifier contract |
|
|
402
|
-
| `verifyZkmlModel(id, calldata)` | Verify ML model proof on-chain |
|
|
403
|
-
| `isZkmlProofVerified(hash)` | Check if proof is verified |
|
|
404
|
-
| `getZkmlVerificationCount(id)` | Get verification count for model |
|
|
405
|
-
| `getZkmlModelCommitment(id)` | Get registered weight commitment |
|
|
406
|
-
| `proveAndVerifyOnChain(prover, req)` | End-to-end: prove via API + verify on-chain |
|
|
389
|
+
## Denomination Validation
|
|
407
390
|
|
|
408
|
-
|
|
391
|
+
Privacy pool and VM31 vault deposits must use standard denominations to prevent exact-amount correlation attacks. The SDK enforces this client-side.
|
|
409
392
|
|
|
410
|
-
|
|
393
|
+
```typescript
|
|
394
|
+
import {
|
|
395
|
+
validateDenomination,
|
|
396
|
+
splitIntoDenominations,
|
|
397
|
+
getDenominations,
|
|
398
|
+
ETH_DENOMINATIONS,
|
|
399
|
+
} from '@obelyzk/sdk/obelysk';
|
|
400
|
+
|
|
401
|
+
// Validate a deposit amount
|
|
402
|
+
validateDenomination(10_000_000_000_000_000n, 'eth'); // 0.01 ETH -- OK
|
|
403
|
+
validateDenomination(12_345_678n, 'eth'); // throws Error
|
|
404
|
+
|
|
405
|
+
// Auto-split an arbitrary amount into denomination-safe batches
|
|
406
|
+
const { denominations, remainder } = splitIntoDenominations(
|
|
407
|
+
350_000_000_000_000_000n, // 0.35 ETH
|
|
408
|
+
'eth',
|
|
409
|
+
);
|
|
410
|
+
// denominations = [0.1 ETH, 0.1 ETH, 0.1 ETH, 0.05 ETH]
|
|
411
|
+
// remainder = 0n
|
|
411
412
|
|
|
412
|
-
|
|
413
|
+
// List valid denominations for an asset
|
|
414
|
+
const denoms = getDenominations('usdc');
|
|
415
|
+
// [1 USDC, 5 USDC, 10 USDC, 50 USDC, 100 USDC, 500 USDC]
|
|
416
|
+
```
|
|
413
417
|
|
|
414
|
-
|
|
418
|
+
Supported tokens: wBTC, SAGE, ETH, STRK, USDC. Unknown assets pass through without restriction.
|
|
415
419
|
|
|
416
|
-
|
|
417
|
-
import { createStwoProverClient } from '@bitsage/sdk';
|
|
420
|
+
---
|
|
418
421
|
|
|
419
|
-
|
|
420
|
-
baseUrl: 'http://your-gpu-server:8080',
|
|
421
|
-
});
|
|
422
|
+
## Contract Addresses
|
|
422
423
|
|
|
423
|
-
|
|
424
|
-
const model = await prover.loadZkmlModel({
|
|
425
|
-
modelPath: '/path/to/model.onnx',
|
|
426
|
-
description: 'Qwen3-14B block 0',
|
|
427
|
-
});
|
|
428
|
-
console.log('Model ID:', model.modelId);
|
|
429
|
-
console.log('Weight commitment:', model.weightCommitment);
|
|
430
|
-
|
|
431
|
-
// Prove with progress tracking
|
|
432
|
-
const result = await prover.proveZkml(
|
|
433
|
-
{ modelId: model.modelId, gpu: true },
|
|
434
|
-
{
|
|
435
|
-
onProgress: (status) => {
|
|
436
|
-
console.log(`${(status.progressBps / 100).toFixed(1)}% — ${status.elapsedSecs.toFixed(1)}s`);
|
|
437
|
-
},
|
|
438
|
-
pollIntervalMs: 2000,
|
|
439
|
-
timeoutMs: 300_000,
|
|
440
|
-
}
|
|
441
|
-
);
|
|
424
|
+
All mainnet and Sepolia addresses are available programmatically:
|
|
442
425
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
426
|
+
```typescript
|
|
427
|
+
import {
|
|
428
|
+
getContracts,
|
|
429
|
+
getRpcUrl,
|
|
430
|
+
MAINNET_TOKENS,
|
|
431
|
+
MAINNET_PRIVACY_POOLS,
|
|
432
|
+
TOKEN_DECIMALS,
|
|
433
|
+
DARKPOOL_ASSET_IDS,
|
|
434
|
+
VM31_ASSET_IDS,
|
|
435
|
+
parseAmount,
|
|
436
|
+
formatAmount,
|
|
437
|
+
} from '@obelyzk/sdk/obelysk';
|
|
438
|
+
|
|
439
|
+
const contracts = getContracts('mainnet');
|
|
440
|
+
console.log('SAGE Token:', contracts.sage_token);
|
|
441
|
+
console.log('DarkPool:', contracts.dark_pool);
|
|
442
|
+
console.log('VM31 Pool:', contracts.vm31_pool);
|
|
443
|
+
|
|
444
|
+
// Token utilities
|
|
445
|
+
const wei = parseAmount('1.5', 'eth'); // 1500000000000000000n
|
|
446
|
+
const str = formatAmount(wei, 'eth'); // "1.5"
|
|
446
447
|
```
|
|
447
448
|
|
|
448
|
-
###
|
|
449
|
+
### Mainnet Contracts (14 total)
|
|
450
|
+
|
|
451
|
+
| Contract | Address |
|
|
452
|
+
|---|---|
|
|
453
|
+
| SAGE Token | `0x0098d563...931c799` |
|
|
454
|
+
| ConfidentialTransfer | `0x0673685b...a979d2` |
|
|
455
|
+
| PrivacyRouter | `0x00f3fd87...bfbdfbd` |
|
|
456
|
+
| ProverStaking | `0x07d2ecff...293e3` |
|
|
457
|
+
| DarkPool | `0x0230b582...ea7727` |
|
|
458
|
+
| ShieldedSwap | `0x05a7f8a6...29725b` |
|
|
459
|
+
| StealthRegistry | `0x077ee4c3...fcaea8` |
|
|
460
|
+
| OTC Orderbook | `0x04165f8f...941b19` |
|
|
461
|
+
| SumcheckVerifier | `0x05071a94...d330e8` |
|
|
462
|
+
| VM31Pool | `0x0230eb35...1400d` |
|
|
463
|
+
| VM31Bridge | `0x048f481c...0356c` |
|
|
464
|
+
| PrivacyPool (wBTC) | `0x030fcfd4...d5e5e2` |
|
|
465
|
+
| PrivacyPool (SAGE) | `0x0224977...ac724f` |
|
|
466
|
+
| PrivacyPool (ETH) | `0x06d0b41c...e82b5` |
|
|
467
|
+
| PrivacyPool (STRK) | `0x02c348e8...c9cf1` |
|
|
468
|
+
| PrivacyPool (USDC) | `0x05d36d7f...d4d59b` |
|
|
449
469
|
|
|
450
|
-
|
|
451
|
-
import { createStwoClient } from '@bitsage/sdk';
|
|
470
|
+
---
|
|
452
471
|
|
|
453
|
-
|
|
472
|
+
## React Hooks
|
|
454
473
|
|
|
455
|
-
|
|
456
|
-
|
|
474
|
+
```typescript
|
|
475
|
+
import { useBitSage, usePrivacy, useStwoProver } from '@obelyzk/sdk/react';
|
|
457
476
|
|
|
458
|
-
|
|
459
|
-
|
|
477
|
+
function App() {
|
|
478
|
+
const { client, isConnected } = useBitSage();
|
|
479
|
+
const { privacy, keyPair, generateKeyPair } = usePrivacy();
|
|
480
|
+
const { prover, submitJob, status } = useStwoProver();
|
|
460
481
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
482
|
+
return (
|
|
483
|
+
<div>
|
|
484
|
+
<p>Connected: {String(isConnected)}</p>
|
|
485
|
+
<button onClick={generateKeyPair}>Generate Keys</button>
|
|
486
|
+
</div>
|
|
487
|
+
);
|
|
488
|
+
}
|
|
464
489
|
```
|
|
465
490
|
|
|
466
|
-
|
|
491
|
+
Requires `react >= 18` as a peer dependency (optional -- only needed if you use this entry point).
|
|
467
492
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
```
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Requirements
|
|
496
|
+
|
|
497
|
+
- Node.js >= 18
|
|
498
|
+
- `starknet` >= 6.0.0
|
|
499
|
+
- TypeScript >= 5.0 (recommended)
|
|
476
500
|
|
|
477
501
|
---
|
|
478
502
|
|