@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 +21 -0
- package/README.md +187 -0
- package/dist/routes/index.d.ts +5 -0
- package/dist/routes/index.js +378 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +483 -0
- package/package.json +59 -0
- package/src/middleware/error-handler.ts +59 -0
- package/src/middleware/index.ts +2 -0
- package/src/middleware/validation.ts +84 -0
- package/src/routes/commitment.ts +64 -0
- package/src/routes/health.ts +54 -0
- package/src/routes/index.ts +18 -0
- package/src/routes/proof.ts +82 -0
- package/src/routes/stealth.ts +76 -0
- package/src/routes/swap.ts +262 -0
- package/src/server.ts +76 -0
- package/src/types/api.ts +123 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/server.ts
|
|
31
|
+
var server_exports = {};
|
|
32
|
+
__export(server_exports, {
|
|
33
|
+
default: () => server_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(server_exports);
|
|
36
|
+
var import_express7 = __toESM(require("express"));
|
|
37
|
+
var import_cors = __toESM(require("cors"));
|
|
38
|
+
var import_helmet = __toESM(require("helmet"));
|
|
39
|
+
var import_compression = __toESM(require("compression"));
|
|
40
|
+
var import_morgan = __toESM(require("morgan"));
|
|
41
|
+
|
|
42
|
+
// src/routes/index.ts
|
|
43
|
+
var import_express6 = require("express");
|
|
44
|
+
|
|
45
|
+
// src/routes/health.ts
|
|
46
|
+
var import_express = require("express");
|
|
47
|
+
var router = (0, import_express.Router)();
|
|
48
|
+
var startTime = Date.now();
|
|
49
|
+
router.get("/", (req, res) => {
|
|
50
|
+
const response = {
|
|
51
|
+
success: true,
|
|
52
|
+
data: {
|
|
53
|
+
status: "healthy",
|
|
54
|
+
version: process.env.npm_package_version || "0.1.0",
|
|
55
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
56
|
+
uptime: Math.floor((Date.now() - startTime) / 1e3)
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
res.json(response);
|
|
60
|
+
});
|
|
61
|
+
var health_default = router;
|
|
62
|
+
|
|
63
|
+
// src/routes/stealth.ts
|
|
64
|
+
var import_express2 = require("express");
|
|
65
|
+
var import_sdk2 = require("@sip-protocol/sdk");
|
|
66
|
+
|
|
67
|
+
// src/middleware/error-handler.ts
|
|
68
|
+
var import_sdk = require("@sip-protocol/sdk");
|
|
69
|
+
function errorHandler(err, req, res, next) {
|
|
70
|
+
console.error("[API Error]", {
|
|
71
|
+
path: req.path,
|
|
72
|
+
method: req.method,
|
|
73
|
+
error: err.message,
|
|
74
|
+
stack: err.stack
|
|
75
|
+
});
|
|
76
|
+
if ((0, import_sdk.isSIPError)(err)) {
|
|
77
|
+
const sipError = err;
|
|
78
|
+
return res.status(400).json({
|
|
79
|
+
success: false,
|
|
80
|
+
error: {
|
|
81
|
+
code: sipError.code,
|
|
82
|
+
message: sipError.message,
|
|
83
|
+
details: {
|
|
84
|
+
field: sipError.field,
|
|
85
|
+
expected: sipError.expected
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
res.status(500).json({
|
|
91
|
+
success: false,
|
|
92
|
+
error: {
|
|
93
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
94
|
+
message: "An unexpected error occurred",
|
|
95
|
+
details: process.env.NODE_ENV === "development" ? err.message : void 0
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function notFoundHandler(req, res) {
|
|
100
|
+
res.status(404).json({
|
|
101
|
+
success: false,
|
|
102
|
+
error: {
|
|
103
|
+
code: "NOT_FOUND",
|
|
104
|
+
message: `Route ${req.method} ${req.path} not found`
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/middleware/validation.ts
|
|
110
|
+
var import_zod = require("zod");
|
|
111
|
+
function validateRequest(schema) {
|
|
112
|
+
return async (req, res, next) => {
|
|
113
|
+
try {
|
|
114
|
+
if (schema.body) {
|
|
115
|
+
req.body = await schema.body.parseAsync(req.body);
|
|
116
|
+
}
|
|
117
|
+
if (schema.query) {
|
|
118
|
+
req.query = await schema.query.parseAsync(req.query);
|
|
119
|
+
}
|
|
120
|
+
if (schema.params) {
|
|
121
|
+
req.params = await schema.params.parseAsync(req.params);
|
|
122
|
+
}
|
|
123
|
+
next();
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (error instanceof import_zod.z.ZodError) {
|
|
126
|
+
return res.status(400).json({
|
|
127
|
+
success: false,
|
|
128
|
+
error: {
|
|
129
|
+
code: "VALIDATION_ERROR",
|
|
130
|
+
message: "Invalid request data",
|
|
131
|
+
details: error.errors
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
next(error);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
var schemas = {
|
|
140
|
+
generateStealth: import_zod.z.object({
|
|
141
|
+
chain: import_zod.z.enum(["solana", "ethereum", "near", "zcash", "polygon", "arbitrum", "optimism", "base", "bitcoin", "aptos", "sui", "cosmos", "osmosis", "injective", "celestia", "sei", "dydx"]),
|
|
142
|
+
recipientMetaAddress: import_zod.z.object({
|
|
143
|
+
spendingKey: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/),
|
|
144
|
+
viewingKey: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/),
|
|
145
|
+
chain: import_zod.z.enum(["solana", "ethereum", "near", "zcash", "polygon", "arbitrum", "optimism", "base", "bitcoin", "aptos", "sui", "cosmos", "osmosis", "injective", "celestia", "sei", "dydx"]),
|
|
146
|
+
label: import_zod.z.string().optional()
|
|
147
|
+
})
|
|
148
|
+
}),
|
|
149
|
+
createCommitment: import_zod.z.object({
|
|
150
|
+
value: import_zod.z.string().regex(/^\d+$/),
|
|
151
|
+
// bigint as string
|
|
152
|
+
blindingFactor: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/).optional()
|
|
153
|
+
}),
|
|
154
|
+
generateFundingProof: import_zod.z.object({
|
|
155
|
+
balance: import_zod.z.string().regex(/^\d+$/),
|
|
156
|
+
minRequired: import_zod.z.string().regex(/^\d+$/),
|
|
157
|
+
balanceBlinding: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/)
|
|
158
|
+
}),
|
|
159
|
+
getQuote: import_zod.z.object({
|
|
160
|
+
inputChain: import_zod.z.enum(["solana", "ethereum", "near", "zcash", "polygon", "arbitrum", "optimism", "base", "bitcoin", "aptos", "sui", "cosmos", "osmosis", "injective", "celestia", "sei", "dydx"]),
|
|
161
|
+
inputToken: import_zod.z.string().min(1),
|
|
162
|
+
inputAmount: import_zod.z.string().regex(/^\d+$/),
|
|
163
|
+
outputChain: import_zod.z.enum(["solana", "ethereum", "near", "zcash", "polygon", "arbitrum", "optimism", "base", "bitcoin", "aptos", "sui", "cosmos", "osmosis", "injective", "celestia", "sei", "dydx"]),
|
|
164
|
+
outputToken: import_zod.z.string().min(1),
|
|
165
|
+
slippageTolerance: import_zod.z.number().min(0).max(100).optional()
|
|
166
|
+
}),
|
|
167
|
+
executeSwap: import_zod.z.object({
|
|
168
|
+
intentId: import_zod.z.string().min(1),
|
|
169
|
+
quoteId: import_zod.z.string().min(1),
|
|
170
|
+
privacy: import_zod.z.enum(["transparent", "shielded", "compliant"]).optional(),
|
|
171
|
+
viewingKey: import_zod.z.string().regex(/^0x[0-9a-fA-F]+$/).optional()
|
|
172
|
+
}),
|
|
173
|
+
swapStatus: import_zod.z.object({
|
|
174
|
+
id: import_zod.z.string().min(1)
|
|
175
|
+
})
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// src/routes/stealth.ts
|
|
179
|
+
var router2 = (0, import_express2.Router)();
|
|
180
|
+
router2.post(
|
|
181
|
+
"/generate",
|
|
182
|
+
validateRequest({ body: schemas.generateStealth }),
|
|
183
|
+
async (req, res) => {
|
|
184
|
+
const { chain, recipientMetaAddress } = req.body;
|
|
185
|
+
const result = (0, import_sdk2.generateStealthAddress)(recipientMetaAddress);
|
|
186
|
+
const response = {
|
|
187
|
+
success: true,
|
|
188
|
+
data: {
|
|
189
|
+
stealthAddress: {
|
|
190
|
+
address: result.stealthAddress.address,
|
|
191
|
+
ephemeralPublicKey: result.stealthAddress.ephemeralPublicKey,
|
|
192
|
+
viewTag: result.stealthAddress.viewTag
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
res.json(response);
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
var stealth_default = router2;
|
|
200
|
+
|
|
201
|
+
// src/routes/commitment.ts
|
|
202
|
+
var import_express3 = require("express");
|
|
203
|
+
var import_sdk3 = require("@sip-protocol/sdk");
|
|
204
|
+
|
|
205
|
+
// ../../../../node_modules/@noble/hashes/esm/utils.js
|
|
206
|
+
var hasHexBuiltin = /* @__PURE__ */ (() => (
|
|
207
|
+
// @ts-ignore
|
|
208
|
+
typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
|
|
209
|
+
))();
|
|
210
|
+
var asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
|
|
211
|
+
function asciiToBase16(ch) {
|
|
212
|
+
if (ch >= asciis._0 && ch <= asciis._9)
|
|
213
|
+
return ch - asciis._0;
|
|
214
|
+
if (ch >= asciis.A && ch <= asciis.F)
|
|
215
|
+
return ch - (asciis.A - 10);
|
|
216
|
+
if (ch >= asciis.a && ch <= asciis.f)
|
|
217
|
+
return ch - (asciis.a - 10);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
function hexToBytes(hex) {
|
|
221
|
+
if (typeof hex !== "string")
|
|
222
|
+
throw new Error("hex string expected, got " + typeof hex);
|
|
223
|
+
if (hasHexBuiltin)
|
|
224
|
+
return Uint8Array.fromHex(hex);
|
|
225
|
+
const hl = hex.length;
|
|
226
|
+
const al = hl / 2;
|
|
227
|
+
if (hl % 2)
|
|
228
|
+
throw new Error("hex string expected, got unpadded hex of length " + hl);
|
|
229
|
+
const array = new Uint8Array(al);
|
|
230
|
+
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
231
|
+
const n1 = asciiToBase16(hex.charCodeAt(hi));
|
|
232
|
+
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
|
|
233
|
+
if (n1 === void 0 || n2 === void 0) {
|
|
234
|
+
const char = hex[hi] + hex[hi + 1];
|
|
235
|
+
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
|
236
|
+
}
|
|
237
|
+
array[ai] = n1 * 16 + n2;
|
|
238
|
+
}
|
|
239
|
+
return array;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/routes/commitment.ts
|
|
243
|
+
var router3 = (0, import_express3.Router)();
|
|
244
|
+
router3.post(
|
|
245
|
+
"/create",
|
|
246
|
+
validateRequest({ body: schemas.createCommitment }),
|
|
247
|
+
async (req, res) => {
|
|
248
|
+
const { value, blindingFactor } = req.body;
|
|
249
|
+
const valueBigInt = BigInt(value);
|
|
250
|
+
const blindingBytes = blindingFactor ? hexToBytes(blindingFactor) : void 0;
|
|
251
|
+
const result = (0, import_sdk3.commit)(valueBigInt, blindingBytes);
|
|
252
|
+
const response = {
|
|
253
|
+
success: true,
|
|
254
|
+
data: {
|
|
255
|
+
commitment: result.commitment,
|
|
256
|
+
blindingFactor: result.blinding
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
res.json(response);
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
var commitment_default = router3;
|
|
263
|
+
|
|
264
|
+
// src/routes/proof.ts
|
|
265
|
+
var import_express4 = require("express");
|
|
266
|
+
var import_sdk4 = require("@sip-protocol/sdk");
|
|
267
|
+
var router4 = (0, import_express4.Router)();
|
|
268
|
+
var proofProvider = new import_sdk4.MockProofProvider();
|
|
269
|
+
router4.post(
|
|
270
|
+
"/funding",
|
|
271
|
+
validateRequest({ body: schemas.generateFundingProof }),
|
|
272
|
+
async (req, res) => {
|
|
273
|
+
const { balance, minRequired, balanceBlinding } = req.body;
|
|
274
|
+
const balanceBigInt = BigInt(balance);
|
|
275
|
+
const minRequiredBigInt = BigInt(minRequired);
|
|
276
|
+
const balanceBlindingBytes = hexToBytes(balanceBlinding);
|
|
277
|
+
const result = await proofProvider.generateFundingProof({
|
|
278
|
+
balance: balanceBigInt,
|
|
279
|
+
minimumRequired: minRequiredBigInt,
|
|
280
|
+
blindingFactor: balanceBlindingBytes,
|
|
281
|
+
assetId: "SOL",
|
|
282
|
+
// Default asset
|
|
283
|
+
userAddress: "0x0000000000000000000000000000000000000000",
|
|
284
|
+
ownershipSignature: new Uint8Array(64)
|
|
285
|
+
});
|
|
286
|
+
const response = {
|
|
287
|
+
success: true,
|
|
288
|
+
data: {
|
|
289
|
+
proof: result.proof.proof,
|
|
290
|
+
publicInputs: result.proof.publicInputs
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
res.json(response);
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
var proof_default = router4;
|
|
297
|
+
|
|
298
|
+
// src/routes/swap.ts
|
|
299
|
+
var import_express5 = require("express");
|
|
300
|
+
var import_sdk5 = require("@sip-protocol/sdk");
|
|
301
|
+
var router5 = (0, import_express5.Router)();
|
|
302
|
+
var sip = new import_sdk5.SIP({ network: "testnet" });
|
|
303
|
+
var swaps = /* @__PURE__ */ new Map();
|
|
304
|
+
router5.post(
|
|
305
|
+
"/",
|
|
306
|
+
validateRequest({ body: schemas.getQuote }),
|
|
307
|
+
async (req, res) => {
|
|
308
|
+
const {
|
|
309
|
+
inputChain,
|
|
310
|
+
inputToken,
|
|
311
|
+
inputAmount,
|
|
312
|
+
outputChain,
|
|
313
|
+
outputToken,
|
|
314
|
+
slippageTolerance
|
|
315
|
+
} = req.body;
|
|
316
|
+
const intent = await sip.createIntent({
|
|
317
|
+
input: {
|
|
318
|
+
asset: {
|
|
319
|
+
chain: inputChain,
|
|
320
|
+
address: null,
|
|
321
|
+
// Native token
|
|
322
|
+
symbol: inputToken,
|
|
323
|
+
decimals: 9
|
|
324
|
+
},
|
|
325
|
+
amount: BigInt(inputAmount)
|
|
326
|
+
},
|
|
327
|
+
output: {
|
|
328
|
+
asset: {
|
|
329
|
+
chain: outputChain,
|
|
330
|
+
address: null,
|
|
331
|
+
// Native token
|
|
332
|
+
symbol: outputToken,
|
|
333
|
+
decimals: 9
|
|
334
|
+
},
|
|
335
|
+
minAmount: BigInt(inputAmount) * 95n / 100n,
|
|
336
|
+
// 5% slippage
|
|
337
|
+
maxSlippage: (slippageTolerance || 1) / 100
|
|
338
|
+
},
|
|
339
|
+
privacy: import_sdk5.PrivacyLevel.TRANSPARENT
|
|
340
|
+
// Default to transparent for quote
|
|
341
|
+
});
|
|
342
|
+
const mockQuote = {
|
|
343
|
+
quoteId: `quote-${Date.now()}`,
|
|
344
|
+
inputAmount,
|
|
345
|
+
outputAmount: (BigInt(inputAmount) * 95n / 100n).toString(),
|
|
346
|
+
// Mock 5% fee
|
|
347
|
+
rate: "0.95",
|
|
348
|
+
estimatedTime: 30,
|
|
349
|
+
fees: {
|
|
350
|
+
network: "0.001",
|
|
351
|
+
protocol: "0.003"
|
|
352
|
+
},
|
|
353
|
+
route: {
|
|
354
|
+
steps: [
|
|
355
|
+
{
|
|
356
|
+
chain: inputChain,
|
|
357
|
+
protocol: "NEAR Intents",
|
|
358
|
+
fromToken: inputToken,
|
|
359
|
+
toToken: outputToken
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const response = {
|
|
365
|
+
success: true,
|
|
366
|
+
data: mockQuote
|
|
367
|
+
};
|
|
368
|
+
res.json(response);
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
router5.post(
|
|
372
|
+
"/swap",
|
|
373
|
+
validateRequest({ body: schemas.executeSwap }),
|
|
374
|
+
async (req, res) => {
|
|
375
|
+
const { intentId, quoteId, privacy, viewingKey } = req.body;
|
|
376
|
+
const swapId = `swap-${Date.now()}`;
|
|
377
|
+
const swap = {
|
|
378
|
+
id: swapId,
|
|
379
|
+
status: "pending",
|
|
380
|
+
inputAmount: "1000000000",
|
|
381
|
+
// Mock value
|
|
382
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
383
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
384
|
+
};
|
|
385
|
+
swaps.set(swapId, swap);
|
|
386
|
+
const response = {
|
|
387
|
+
success: true,
|
|
388
|
+
data: {
|
|
389
|
+
swapId,
|
|
390
|
+
status: "pending",
|
|
391
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
res.json(response);
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
router5.get(
|
|
398
|
+
"/:id/status",
|
|
399
|
+
validateRequest({ params: schemas.swapStatus }),
|
|
400
|
+
async (req, res) => {
|
|
401
|
+
const { id } = req.params;
|
|
402
|
+
const swap = swaps.get(id);
|
|
403
|
+
if (!swap) {
|
|
404
|
+
return res.status(404).json({
|
|
405
|
+
success: false,
|
|
406
|
+
error: {
|
|
407
|
+
code: "SWAP_NOT_FOUND",
|
|
408
|
+
message: `Swap ${id} not found`
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
const response = {
|
|
413
|
+
success: true,
|
|
414
|
+
data: swap
|
|
415
|
+
};
|
|
416
|
+
res.json(response);
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
var swap_default = router5;
|
|
420
|
+
|
|
421
|
+
// src/routes/index.ts
|
|
422
|
+
var router6 = (0, import_express6.Router)();
|
|
423
|
+
router6.use("/health", health_default);
|
|
424
|
+
router6.use("/stealth", stealth_default);
|
|
425
|
+
router6.use("/commitment", commitment_default);
|
|
426
|
+
router6.use("/proof", proof_default);
|
|
427
|
+
router6.use("/quote", swap_default);
|
|
428
|
+
router6.use("/swap", swap_default);
|
|
429
|
+
var routes_default = router6;
|
|
430
|
+
|
|
431
|
+
// src/server.ts
|
|
432
|
+
var app = (0, import_express7.default)();
|
|
433
|
+
var PORT = process.env.PORT || 3e3;
|
|
434
|
+
var NODE_ENV = process.env.NODE_ENV || "development";
|
|
435
|
+
var CORS_ORIGIN = process.env.CORS_ORIGIN || "*";
|
|
436
|
+
app.use((0, import_helmet.default)());
|
|
437
|
+
app.use((0, import_cors.default)({
|
|
438
|
+
origin: CORS_ORIGIN,
|
|
439
|
+
credentials: true
|
|
440
|
+
}));
|
|
441
|
+
app.use(import_express7.default.json({ limit: "1mb" }));
|
|
442
|
+
app.use(import_express7.default.urlencoded({ extended: true, limit: "1mb" }));
|
|
443
|
+
app.use((0, import_compression.default)());
|
|
444
|
+
app.use((0, import_morgan.default)(NODE_ENV === "development" ? "dev" : "combined"));
|
|
445
|
+
app.use("/api/v1", routes_default);
|
|
446
|
+
app.get("/", (req, res) => {
|
|
447
|
+
res.json({
|
|
448
|
+
name: "@sip-protocol/api",
|
|
449
|
+
version: "0.1.0",
|
|
450
|
+
description: "REST API service for SIP Protocol SDK",
|
|
451
|
+
documentation: "/api/v1/health",
|
|
452
|
+
endpoints: {
|
|
453
|
+
health: "GET /api/v1/health",
|
|
454
|
+
stealth: "POST /api/v1/stealth/generate",
|
|
455
|
+
commitment: "POST /api/v1/commitment/create",
|
|
456
|
+
proof: "POST /api/v1/proof/funding",
|
|
457
|
+
quote: "POST /api/v1/quote",
|
|
458
|
+
swap: "POST /api/v1/swap",
|
|
459
|
+
swapStatus: "GET /api/v1/swap/:id/status"
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
app.use(notFoundHandler);
|
|
464
|
+
app.use(errorHandler);
|
|
465
|
+
if (require.main === module) {
|
|
466
|
+
app.listen(PORT, () => {
|
|
467
|
+
console.log(`
|
|
468
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
469
|
+
\u2551 SIP Protocol REST API \u2551
|
|
470
|
+
\u2551 Version: 0.1.0 \u2551
|
|
471
|
+
\u2551 Port: ${PORT} \u2551
|
|
472
|
+
\u2551 Environment: ${NODE_ENV} \u2551
|
|
473
|
+
\u2551 Documentation: http://localhost:${PORT}/ \u2551
|
|
474
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
475
|
+
`);
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
var server_default = app;
|
|
479
|
+
/*! Bundled license information:
|
|
480
|
+
|
|
481
|
+
@noble/hashes/esm/utils.js:
|
|
482
|
+
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
483
|
+
*/
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sip-protocol/api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "REST API service for SIP Protocol SDK - Optional wrapper for non-JS backends",
|
|
5
|
+
"author": "SIP Protocol <hello@sip-protocol.org>",
|
|
6
|
+
"homepage": "https://sip-protocol.org",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/sip-protocol/sip-protocol.git",
|
|
10
|
+
"directory": "packages/api"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/sip-protocol/sip-protocol/issues"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/server.js",
|
|
16
|
+
"types": "./dist/server.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@sip-protocol/sdk": "^0.6.0",
|
|
23
|
+
"@sip-protocol/types": "^0.2.0",
|
|
24
|
+
"compression": "^1.8.1",
|
|
25
|
+
"cors": "^2.8.5",
|
|
26
|
+
"express": "^4.22.1",
|
|
27
|
+
"helmet": "^7.2.0",
|
|
28
|
+
"morgan": "^1.10.1",
|
|
29
|
+
"zod": "^3.25.76"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/compression": "^1.8.1",
|
|
33
|
+
"@types/cors": "^2.8.19",
|
|
34
|
+
"@types/express": "^4.17.25",
|
|
35
|
+
"@types/morgan": "^1.9.10",
|
|
36
|
+
"tsup": "^8.5.1",
|
|
37
|
+
"tsx": "^4.21.0",
|
|
38
|
+
"typescript": "^5.3.0",
|
|
39
|
+
"vitest": "^1.1.0"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"sip",
|
|
43
|
+
"privacy",
|
|
44
|
+
"rest-api",
|
|
45
|
+
"stealth-addresses",
|
|
46
|
+
"commitments",
|
|
47
|
+
"api"
|
|
48
|
+
],
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"scripts": {
|
|
51
|
+
"start": "node dist/server.js",
|
|
52
|
+
"dev": "tsx watch src/server.ts",
|
|
53
|
+
"build": "tsup src/server.ts src/routes/index.ts --format cjs --dts",
|
|
54
|
+
"lint": "eslint --ext .ts src/",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"clean": "rm -rf dist",
|
|
57
|
+
"test": "vitest"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express'
|
|
2
|
+
import { SIPError, isSIPError } from '@sip-protocol/sdk'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Global error handler middleware
|
|
6
|
+
*/
|
|
7
|
+
export function errorHandler(
|
|
8
|
+
err: Error,
|
|
9
|
+
req: Request,
|
|
10
|
+
res: Response,
|
|
11
|
+
next: NextFunction
|
|
12
|
+
) {
|
|
13
|
+
// Log error for debugging
|
|
14
|
+
console.error('[API Error]', {
|
|
15
|
+
path: req.path,
|
|
16
|
+
method: req.method,
|
|
17
|
+
error: err.message,
|
|
18
|
+
stack: err.stack,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Handle SIP SDK errors
|
|
22
|
+
if (isSIPError(err)) {
|
|
23
|
+
const sipError = err as SIPError
|
|
24
|
+
return res.status(400).json({
|
|
25
|
+
success: false,
|
|
26
|
+
error: {
|
|
27
|
+
code: sipError.code,
|
|
28
|
+
message: sipError.message,
|
|
29
|
+
details: {
|
|
30
|
+
field: (sipError as any).field,
|
|
31
|
+
expected: (sipError as any).expected,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Handle generic errors
|
|
38
|
+
res.status(500).json({
|
|
39
|
+
success: false,
|
|
40
|
+
error: {
|
|
41
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
42
|
+
message: 'An unexpected error occurred',
|
|
43
|
+
details: process.env.NODE_ENV === 'development' ? err.message : undefined,
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 404 handler
|
|
50
|
+
*/
|
|
51
|
+
export function notFoundHandler(req: Request, res: Response) {
|
|
52
|
+
res.status(404).json({
|
|
53
|
+
success: false,
|
|
54
|
+
error: {
|
|
55
|
+
code: 'NOT_FOUND',
|
|
56
|
+
message: `Route ${req.method} ${req.path} not found`,
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express'
|
|
2
|
+
import { z, ZodSchema } from 'zod'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Zod schema validation middleware factory
|
|
6
|
+
*/
|
|
7
|
+
export function validateRequest(schema: {
|
|
8
|
+
body?: ZodSchema
|
|
9
|
+
query?: ZodSchema
|
|
10
|
+
params?: ZodSchema
|
|
11
|
+
}) {
|
|
12
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
13
|
+
try {
|
|
14
|
+
if (schema.body) {
|
|
15
|
+
req.body = await schema.body.parseAsync(req.body)
|
|
16
|
+
}
|
|
17
|
+
if (schema.query) {
|
|
18
|
+
req.query = await schema.query.parseAsync(req.query)
|
|
19
|
+
}
|
|
20
|
+
if (schema.params) {
|
|
21
|
+
req.params = await schema.params.parseAsync(req.params)
|
|
22
|
+
}
|
|
23
|
+
next()
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error instanceof z.ZodError) {
|
|
26
|
+
return res.status(400).json({
|
|
27
|
+
success: false,
|
|
28
|
+
error: {
|
|
29
|
+
code: 'VALIDATION_ERROR',
|
|
30
|
+
message: 'Invalid request data',
|
|
31
|
+
details: error.errors,
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
next(error)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Validation schemas
|
|
42
|
+
*/
|
|
43
|
+
export const schemas = {
|
|
44
|
+
generateStealth: z.object({
|
|
45
|
+
chain: z.enum(['solana', 'ethereum', 'near', 'zcash', 'polygon', 'arbitrum', 'optimism', 'base', 'bitcoin', 'aptos', 'sui', 'cosmos', 'osmosis', 'injective', 'celestia', 'sei', 'dydx']),
|
|
46
|
+
recipientMetaAddress: z.object({
|
|
47
|
+
spendingKey: z.string().regex(/^0x[0-9a-fA-F]+$/),
|
|
48
|
+
viewingKey: z.string().regex(/^0x[0-9a-fA-F]+$/),
|
|
49
|
+
chain: z.enum(['solana', 'ethereum', 'near', 'zcash', 'polygon', 'arbitrum', 'optimism', 'base', 'bitcoin', 'aptos', 'sui', 'cosmos', 'osmosis', 'injective', 'celestia', 'sei', 'dydx']),
|
|
50
|
+
label: z.string().optional(),
|
|
51
|
+
}),
|
|
52
|
+
}),
|
|
53
|
+
|
|
54
|
+
createCommitment: z.object({
|
|
55
|
+
value: z.string().regex(/^\d+$/), // bigint as string
|
|
56
|
+
blindingFactor: z.string().regex(/^0x[0-9a-fA-F]+$/).optional(),
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
generateFundingProof: z.object({
|
|
60
|
+
balance: z.string().regex(/^\d+$/),
|
|
61
|
+
minRequired: z.string().regex(/^\d+$/),
|
|
62
|
+
balanceBlinding: z.string().regex(/^0x[0-9a-fA-F]+$/),
|
|
63
|
+
}),
|
|
64
|
+
|
|
65
|
+
getQuote: z.object({
|
|
66
|
+
inputChain: z.enum(['solana', 'ethereum', 'near', 'zcash', 'polygon', 'arbitrum', 'optimism', 'base', 'bitcoin', 'aptos', 'sui', 'cosmos', 'osmosis', 'injective', 'celestia', 'sei', 'dydx']),
|
|
67
|
+
inputToken: z.string().min(1),
|
|
68
|
+
inputAmount: z.string().regex(/^\d+$/),
|
|
69
|
+
outputChain: z.enum(['solana', 'ethereum', 'near', 'zcash', 'polygon', 'arbitrum', 'optimism', 'base', 'bitcoin', 'aptos', 'sui', 'cosmos', 'osmosis', 'injective', 'celestia', 'sei', 'dydx']),
|
|
70
|
+
outputToken: z.string().min(1),
|
|
71
|
+
slippageTolerance: z.number().min(0).max(100).optional(),
|
|
72
|
+
}),
|
|
73
|
+
|
|
74
|
+
executeSwap: z.object({
|
|
75
|
+
intentId: z.string().min(1),
|
|
76
|
+
quoteId: z.string().min(1),
|
|
77
|
+
privacy: z.enum(['transparent', 'shielded', 'compliant']).optional(),
|
|
78
|
+
viewingKey: z.string().regex(/^0x[0-9a-fA-F]+$/).optional(),
|
|
79
|
+
}),
|
|
80
|
+
|
|
81
|
+
swapStatus: z.object({
|
|
82
|
+
id: z.string().min(1),
|
|
83
|
+
}),
|
|
84
|
+
}
|