@sip-protocol/api 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 RECTOR Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # @sip-protocol/api
2
+
3
+ REST API service for SIP Protocol SDK - Optional wrapper for non-JavaScript backends.
4
+
5
+ ## Overview
6
+
7
+ This package provides a REST API wrapper around the `@sip-protocol/sdk`, allowing non-JavaScript applications to interact with the SIP Protocol through HTTP requests.
8
+
9
+ ## Features
10
+
11
+ - RESTful API endpoints for all core SDK functionality
12
+ - Request validation with Zod schemas
13
+ - Comprehensive error handling
14
+ - OpenAPI documentation
15
+ - Docker support for easy deployment
16
+ - Production-ready security (Helmet, CORS, rate limiting)
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pnpm install
22
+ ```
23
+
24
+ ## Development
25
+
26
+ ```bash
27
+ # Start development server with hot reload
28
+ pnpm dev
29
+
30
+ # Build for production
31
+ pnpm build
32
+
33
+ # Start production server
34
+ pnpm start
35
+
36
+ # Run tests
37
+ pnpm test
38
+
39
+ # Type check
40
+ pnpm typecheck
41
+ ```
42
+
43
+ ## API Endpoints
44
+
45
+ ### Health Check
46
+ - `GET /api/v1/health` - Service health check
47
+
48
+ ### Stealth Addresses
49
+ - `POST /api/v1/stealth/generate` - Generate stealth address
50
+
51
+ ### Commitments
52
+ - `POST /api/v1/commitment/create` - Create Pedersen commitment
53
+
54
+ ### Proofs
55
+ - `POST /api/v1/proof/funding` - Generate funding proof
56
+
57
+ ### Swaps
58
+ - `POST /api/v1/quote` - Get swap quote
59
+ - `POST /api/v1/swap` - Execute swap
60
+ - `GET /api/v1/swap/:id/status` - Get swap status
61
+
62
+ ## Usage Examples
63
+
64
+ ### Generate Stealth Address
65
+
66
+ ```bash
67
+ curl -X POST http://localhost:3000/api/v1/stealth/generate \
68
+ -H "Content-Type: application/json" \
69
+ -d '{
70
+ "chain": "ethereum",
71
+ "recipientMetaAddress": {
72
+ "spendingKey": "0x02...",
73
+ "viewingKey": "0x03...",
74
+ "chain": "ethereum"
75
+ }
76
+ }'
77
+ ```
78
+
79
+ ### Create Commitment
80
+
81
+ ```bash
82
+ curl -X POST http://localhost:3000/api/v1/commitment/create \
83
+ -H "Content-Type: application/json" \
84
+ -d '{
85
+ "value": "1000000000"
86
+ }'
87
+ ```
88
+
89
+ ### Get Swap Quote
90
+
91
+ ```bash
92
+ curl -X POST http://localhost:3000/api/v1/quote \
93
+ -H "Content-Type: application/json" \
94
+ -d '{
95
+ "inputChain": "ethereum",
96
+ "inputToken": "ETH",
97
+ "inputAmount": "1000000000000000000",
98
+ "outputChain": "solana",
99
+ "outputToken": "SOL",
100
+ "slippageTolerance": 1
101
+ }'
102
+ ```
103
+
104
+ ## Docker Deployment
105
+
106
+ ```bash
107
+ # Build image
108
+ docker build -t sip-api .
109
+
110
+ # Run container
111
+ docker run -p 3000:3000 -e NODE_ENV=production sip-api
112
+
113
+ # With environment variables
114
+ docker run -p 3000:3000 \
115
+ -e NODE_ENV=production \
116
+ -e PORT=3000 \
117
+ -e CORS_ORIGIN=https://example.com \
118
+ sip-api
119
+ ```
120
+
121
+ ## Environment Variables
122
+
123
+ - `PORT` - Server port (default: 3000)
124
+ - `NODE_ENV` - Environment (development/production)
125
+ - `CORS_ORIGIN` - CORS allowed origins (default: *)
126
+
127
+ ## Response Format
128
+
129
+ All endpoints return responses in the following format:
130
+
131
+ ### Success Response
132
+ ```json
133
+ {
134
+ "success": true,
135
+ "data": { ... }
136
+ }
137
+ ```
138
+
139
+ ### Error Response
140
+ ```json
141
+ {
142
+ "success": false,
143
+ "error": {
144
+ "code": "ERROR_CODE",
145
+ "message": "Human-readable error message",
146
+ "details": { ... }
147
+ }
148
+ }
149
+ ```
150
+
151
+ ## Error Codes
152
+
153
+ - `VALIDATION_ERROR` - Invalid request parameters
154
+ - `NOT_FOUND` - Resource not found
155
+ - `INTERNAL_SERVER_ERROR` - Unexpected server error
156
+ - SDK-specific error codes from `@sip-protocol/sdk`
157
+
158
+ ## Architecture
159
+
160
+ ```
161
+ src/
162
+ ├── routes/ # API route handlers
163
+ │ ├── health.ts # Health check
164
+ │ ├── stealth.ts # Stealth address generation
165
+ │ ├── commitment.ts # Pedersen commitments
166
+ │ ├── proof.ts # ZK proof generation
167
+ │ └── swap.ts # Swap quotes and execution
168
+ ├── middleware/ # Express middleware
169
+ │ ├── validation.ts # Request validation
170
+ │ └── error-handler.ts # Error handling
171
+ ├── types/ # TypeScript types
172
+ │ └── api.ts # API request/response types
173
+ └── server.ts # Main server setup
174
+ ```
175
+
176
+ ## Production Considerations
177
+
178
+ 1. **Database**: The in-memory swap tracking should be replaced with a proper database (PostgreSQL, MongoDB, etc.)
179
+ 2. **Authentication**: Add API key or JWT authentication for production use
180
+ 3. **Rate Limiting**: Consider adding rate limiting middleware
181
+ 4. **Logging**: Use structured logging (e.g., Winston, Pino) for production
182
+ 5. **Monitoring**: Add APM tools (e.g., New Relic, Datadog)
183
+ 6. **Load Balancing**: Deploy behind a load balancer for high availability
184
+
185
+ ## License
186
+
187
+ MIT
@@ -0,0 +1,5 @@
1
+ import { Router } from 'express';
2
+
3
+ declare const router: Router;
4
+
5
+ export { router as default };
@@ -0,0 +1,378 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/routes/index.ts
21
+ var routes_exports = {};
22
+ __export(routes_exports, {
23
+ default: () => routes_default
24
+ });
25
+ module.exports = __toCommonJS(routes_exports);
26
+ var import_express6 = require("express");
27
+
28
+ // src/routes/health.ts
29
+ var import_express = require("express");
30
+ var router = (0, import_express.Router)();
31
+ var startTime = Date.now();
32
+ router.get("/", (req, res) => {
33
+ const response = {
34
+ success: true,
35
+ data: {
36
+ status: "healthy",
37
+ version: process.env.npm_package_version || "0.1.0",
38
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
39
+ uptime: Math.floor((Date.now() - startTime) / 1e3)
40
+ }
41
+ };
42
+ res.json(response);
43
+ });
44
+ var health_default = router;
45
+
46
+ // src/routes/stealth.ts
47
+ var import_express2 = require("express");
48
+ var import_sdk2 = require("@sip-protocol/sdk");
49
+
50
+ // src/middleware/error-handler.ts
51
+ var import_sdk = require("@sip-protocol/sdk");
52
+
53
+ // src/middleware/validation.ts
54
+ var import_zod = require("zod");
55
+ function validateRequest(schema) {
56
+ return async (req, res, next) => {
57
+ try {
58
+ if (schema.body) {
59
+ req.body = await schema.body.parseAsync(req.body);
60
+ }
61
+ if (schema.query) {
62
+ req.query = await schema.query.parseAsync(req.query);
63
+ }
64
+ if (schema.params) {
65
+ req.params = await schema.params.parseAsync(req.params);
66
+ }
67
+ next();
68
+ } catch (error) {
69
+ if (error instanceof import_zod.z.ZodError) {
70
+ return res.status(400).json({
71
+ success: false,
72
+ error: {
73
+ code: "VALIDATION_ERROR",
74
+ message: "Invalid request data",
75
+ details: error.errors
76
+ }
77
+ });
78
+ }
79
+ next(error);
80
+ }
81
+ };
82
+ }
83
+ var schemas = {
84
+ generateStealth: import_zod.z.object({
85
+ chain: import_zod.z.enum(["solana", "ethereum", "near", "zcash", "polygon", "arbitrum", "optimism", "base", "bitcoin", "aptos", "sui", "cosmos", "osmosis", "injective", "celestia", "sei", "dydx"]),
86
+ recipientMetaAddress: import_zod.z.object({
87
+ spendingKey: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/),
88
+ viewingKey: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/),
89
+ chain: import_zod.z.enum(["solana", "ethereum", "near", "zcash", "polygon", "arbitrum", "optimism", "base", "bitcoin", "aptos", "sui", "cosmos", "osmosis", "injective", "celestia", "sei", "dydx"]),
90
+ label: import_zod.z.string().optional()
91
+ })
92
+ }),
93
+ createCommitment: import_zod.z.object({
94
+ value: import_zod.z.string().regex(/^\d+$/),
95
+ // bigint as string
96
+ blindingFactor: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/).optional()
97
+ }),
98
+ generateFundingProof: import_zod.z.object({
99
+ balance: import_zod.z.string().regex(/^\d+$/),
100
+ minRequired: import_zod.z.string().regex(/^\d+$/),
101
+ balanceBlinding: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/)
102
+ }),
103
+ getQuote: import_zod.z.object({
104
+ inputChain: import_zod.z.enum(["solana", "ethereum", "near", "zcash", "polygon", "arbitrum", "optimism", "base", "bitcoin", "aptos", "sui", "cosmos", "osmosis", "injective", "celestia", "sei", "dydx"]),
105
+ inputToken: import_zod.z.string().min(1),
106
+ inputAmount: import_zod.z.string().regex(/^\d+$/),
107
+ outputChain: import_zod.z.enum(["solana", "ethereum", "near", "zcash", "polygon", "arbitrum", "optimism", "base", "bitcoin", "aptos", "sui", "cosmos", "osmosis", "injective", "celestia", "sei", "dydx"]),
108
+ outputToken: import_zod.z.string().min(1),
109
+ slippageTolerance: import_zod.z.number().min(0).max(100).optional()
110
+ }),
111
+ executeSwap: import_zod.z.object({
112
+ intentId: import_zod.z.string().min(1),
113
+ quoteId: import_zod.z.string().min(1),
114
+ privacy: import_zod.z.enum(["transparent", "shielded", "compliant"]).optional(),
115
+ viewingKey: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/).optional()
116
+ }),
117
+ swapStatus: import_zod.z.object({
118
+ id: import_zod.z.string().min(1)
119
+ })
120
+ };
121
+
122
+ // src/routes/stealth.ts
123
+ var router2 = (0, import_express2.Router)();
124
+ router2.post(
125
+ "/generate",
126
+ validateRequest({ body: schemas.generateStealth }),
127
+ async (req, res) => {
128
+ const { chain, recipientMetaAddress } = req.body;
129
+ const result = (0, import_sdk2.generateStealthAddress)(recipientMetaAddress);
130
+ const response = {
131
+ success: true,
132
+ data: {
133
+ stealthAddress: {
134
+ address: result.stealthAddress.address,
135
+ ephemeralPublicKey: result.stealthAddress.ephemeralPublicKey,
136
+ viewTag: result.stealthAddress.viewTag
137
+ }
138
+ }
139
+ };
140
+ res.json(response);
141
+ }
142
+ );
143
+ var stealth_default = router2;
144
+
145
+ // src/routes/commitment.ts
146
+ var import_express3 = require("express");
147
+ var import_sdk3 = require("@sip-protocol/sdk");
148
+
149
+ // ../../../../node_modules/@noble/hashes/esm/utils.js
150
+ var hasHexBuiltin = /* @__PURE__ */ (() => (
151
+ // @ts-ignore
152
+ typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
153
+ ))();
154
+ var asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
155
+ function asciiToBase16(ch) {
156
+ if (ch >= asciis._0 && ch <= asciis._9)
157
+ return ch - asciis._0;
158
+ if (ch >= asciis.A && ch <= asciis.F)
159
+ return ch - (asciis.A - 10);
160
+ if (ch >= asciis.a && ch <= asciis.f)
161
+ return ch - (asciis.a - 10);
162
+ return;
163
+ }
164
+ function hexToBytes(hex) {
165
+ if (typeof hex !== "string")
166
+ throw new Error("hex string expected, got " + typeof hex);
167
+ if (hasHexBuiltin)
168
+ return Uint8Array.fromHex(hex);
169
+ const hl = hex.length;
170
+ const al = hl / 2;
171
+ if (hl % 2)
172
+ throw new Error("hex string expected, got unpadded hex of length " + hl);
173
+ const array = new Uint8Array(al);
174
+ for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
175
+ const n1 = asciiToBase16(hex.charCodeAt(hi));
176
+ const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
177
+ if (n1 === void 0 || n2 === void 0) {
178
+ const char = hex[hi] + hex[hi + 1];
179
+ throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
180
+ }
181
+ array[ai] = n1 * 16 + n2;
182
+ }
183
+ return array;
184
+ }
185
+
186
+ // src/routes/commitment.ts
187
+ var router3 = (0, import_express3.Router)();
188
+ router3.post(
189
+ "/create",
190
+ validateRequest({ body: schemas.createCommitment }),
191
+ async (req, res) => {
192
+ const { value, blindingFactor } = req.body;
193
+ const valueBigInt = BigInt(value);
194
+ const blindingBytes = blindingFactor ? hexToBytes(blindingFactor) : void 0;
195
+ const result = (0, import_sdk3.commit)(valueBigInt, blindingBytes);
196
+ const response = {
197
+ success: true,
198
+ data: {
199
+ commitment: result.commitment,
200
+ blindingFactor: result.blinding
201
+ }
202
+ };
203
+ res.json(response);
204
+ }
205
+ );
206
+ var commitment_default = router3;
207
+
208
+ // src/routes/proof.ts
209
+ var import_express4 = require("express");
210
+ var import_sdk4 = require("@sip-protocol/sdk");
211
+ var router4 = (0, import_express4.Router)();
212
+ var proofProvider = new import_sdk4.MockProofProvider();
213
+ router4.post(
214
+ "/funding",
215
+ validateRequest({ body: schemas.generateFundingProof }),
216
+ async (req, res) => {
217
+ const { balance, minRequired, balanceBlinding } = req.body;
218
+ const balanceBigInt = BigInt(balance);
219
+ const minRequiredBigInt = BigInt(minRequired);
220
+ const balanceBlindingBytes = hexToBytes(balanceBlinding);
221
+ const result = await proofProvider.generateFundingProof({
222
+ balance: balanceBigInt,
223
+ minimumRequired: minRequiredBigInt,
224
+ blindingFactor: balanceBlindingBytes,
225
+ assetId: "SOL",
226
+ // Default asset
227
+ userAddress: "0x0000000000000000000000000000000000000000",
228
+ ownershipSignature: new Uint8Array(64)
229
+ });
230
+ const response = {
231
+ success: true,
232
+ data: {
233
+ proof: result.proof.proof,
234
+ publicInputs: result.proof.publicInputs
235
+ }
236
+ };
237
+ res.json(response);
238
+ }
239
+ );
240
+ var proof_default = router4;
241
+
242
+ // src/routes/swap.ts
243
+ var import_express5 = require("express");
244
+ var import_sdk5 = require("@sip-protocol/sdk");
245
+ var router5 = (0, import_express5.Router)();
246
+ var sip = new import_sdk5.SIP({ network: "testnet" });
247
+ var swaps = /* @__PURE__ */ new Map();
248
+ router5.post(
249
+ "/",
250
+ validateRequest({ body: schemas.getQuote }),
251
+ async (req, res) => {
252
+ const {
253
+ inputChain,
254
+ inputToken,
255
+ inputAmount,
256
+ outputChain,
257
+ outputToken,
258
+ slippageTolerance
259
+ } = req.body;
260
+ const intent = await sip.createIntent({
261
+ input: {
262
+ asset: {
263
+ chain: inputChain,
264
+ address: null,
265
+ // Native token
266
+ symbol: inputToken,
267
+ decimals: 9
268
+ },
269
+ amount: BigInt(inputAmount)
270
+ },
271
+ output: {
272
+ asset: {
273
+ chain: outputChain,
274
+ address: null,
275
+ // Native token
276
+ symbol: outputToken,
277
+ decimals: 9
278
+ },
279
+ minAmount: BigInt(inputAmount) * 95n / 100n,
280
+ // 5% slippage
281
+ maxSlippage: (slippageTolerance || 1) / 100
282
+ },
283
+ privacy: import_sdk5.PrivacyLevel.TRANSPARENT
284
+ // Default to transparent for quote
285
+ });
286
+ const mockQuote = {
287
+ quoteId: `quote-${Date.now()}`,
288
+ inputAmount,
289
+ outputAmount: (BigInt(inputAmount) * 95n / 100n).toString(),
290
+ // Mock 5% fee
291
+ rate: "0.95",
292
+ estimatedTime: 30,
293
+ fees: {
294
+ network: "0.001",
295
+ protocol: "0.003"
296
+ },
297
+ route: {
298
+ steps: [
299
+ {
300
+ chain: inputChain,
301
+ protocol: "NEAR Intents",
302
+ fromToken: inputToken,
303
+ toToken: outputToken
304
+ }
305
+ ]
306
+ }
307
+ };
308
+ const response = {
309
+ success: true,
310
+ data: mockQuote
311
+ };
312
+ res.json(response);
313
+ }
314
+ );
315
+ router5.post(
316
+ "/swap",
317
+ validateRequest({ body: schemas.executeSwap }),
318
+ async (req, res) => {
319
+ const { intentId, quoteId, privacy, viewingKey } = req.body;
320
+ const swapId = `swap-${Date.now()}`;
321
+ const swap = {
322
+ id: swapId,
323
+ status: "pending",
324
+ inputAmount: "1000000000",
325
+ // Mock value
326
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
327
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
328
+ };
329
+ swaps.set(swapId, swap);
330
+ const response = {
331
+ success: true,
332
+ data: {
333
+ swapId,
334
+ status: "pending",
335
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
336
+ }
337
+ };
338
+ res.json(response);
339
+ }
340
+ );
341
+ router5.get(
342
+ "/:id/status",
343
+ validateRequest({ params: schemas.swapStatus }),
344
+ async (req, res) => {
345
+ const { id } = req.params;
346
+ const swap = swaps.get(id);
347
+ if (!swap) {
348
+ return res.status(404).json({
349
+ success: false,
350
+ error: {
351
+ code: "SWAP_NOT_FOUND",
352
+ message: `Swap ${id} not found`
353
+ }
354
+ });
355
+ }
356
+ const response = {
357
+ success: true,
358
+ data: swap
359
+ };
360
+ res.json(response);
361
+ }
362
+ );
363
+ var swap_default = router5;
364
+
365
+ // src/routes/index.ts
366
+ var router6 = (0, import_express6.Router)();
367
+ router6.use("/health", health_default);
368
+ router6.use("/stealth", stealth_default);
369
+ router6.use("/commitment", commitment_default);
370
+ router6.use("/proof", proof_default);
371
+ router6.use("/quote", swap_default);
372
+ router6.use("/swap", swap_default);
373
+ var routes_default = router6;
374
+ /*! Bundled license information:
375
+
376
+ @noble/hashes/esm/utils.js:
377
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
378
+ */
@@ -0,0 +1,5 @@
1
+ import { Express } from 'express';
2
+
3
+ declare const app: Express;
4
+
5
+ export { app as default };