@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.
@@ -0,0 +1,64 @@
1
+ import { Router, Request, Response } from 'express'
2
+ import { commit } from '@sip-protocol/sdk'
3
+ import { hexToBytes } from '@noble/hashes/utils'
4
+ import { validateRequest, schemas } from '../middleware'
5
+ import type { CreateCommitmentRequest, CommitmentResponse, ApiResponse } from '../types/api'
6
+
7
+ const router: Router = Router()
8
+
9
+ /**
10
+ * POST /commitment/create
11
+ * Create a Pedersen commitment to a value
12
+ *
13
+ * @openapi
14
+ * /commitment/create:
15
+ * post:
16
+ * summary: Create Pedersen commitment
17
+ * description: Create a cryptographic commitment to hide transaction amounts
18
+ * requestBody:
19
+ * required: true
20
+ * content:
21
+ * application/json:
22
+ * schema:
23
+ * type: object
24
+ * required:
25
+ * - value
26
+ * properties:
27
+ * value:
28
+ * type: string
29
+ * description: Value to commit (as string to handle bigint)
30
+ * example: "1000000000"
31
+ * blindingFactor:
32
+ * type: string
33
+ * description: Optional blinding factor (hex string)
34
+ * pattern: ^0x[0-9a-fA-F]+$
35
+ * responses:
36
+ * 200:
37
+ * description: Commitment created successfully
38
+ * 400:
39
+ * description: Invalid request parameters
40
+ */
41
+ router.post(
42
+ '/create',
43
+ validateRequest({ body: schemas.createCommitment }),
44
+ async (req: Request, res: Response) => {
45
+ const { value, blindingFactor } = req.body as CreateCommitmentRequest
46
+
47
+ const valueBigInt = BigInt(value)
48
+ const blindingBytes = blindingFactor ? hexToBytes(blindingFactor) : undefined
49
+
50
+ const result = commit(valueBigInt, blindingBytes)
51
+
52
+ const response: ApiResponse<CommitmentResponse> = {
53
+ success: true,
54
+ data: {
55
+ commitment: result.commitment,
56
+ blindingFactor: result.blinding,
57
+ },
58
+ }
59
+
60
+ res.json(response)
61
+ }
62
+ )
63
+
64
+ export default router
@@ -0,0 +1,54 @@
1
+ import { Router, Request, Response } from 'express'
2
+ import type { HealthResponse, ApiResponse } from '../types/api'
3
+
4
+ const router: Router = Router()
5
+
6
+ const startTime = Date.now()
7
+
8
+ /**
9
+ * GET /health
10
+ * Health check endpoint
11
+ *
12
+ * @openapi
13
+ * /health:
14
+ * get:
15
+ * summary: Health check
16
+ * description: Check if the API service is running
17
+ * responses:
18
+ * 200:
19
+ * description: Service is healthy
20
+ * content:
21
+ * application/json:
22
+ * schema:
23
+ * type: object
24
+ * properties:
25
+ * success:
26
+ * type: boolean
27
+ * data:
28
+ * type: object
29
+ * properties:
30
+ * status:
31
+ * type: string
32
+ * enum: [healthy]
33
+ * version:
34
+ * type: string
35
+ * timestamp:
36
+ * type: string
37
+ * uptime:
38
+ * type: number
39
+ */
40
+ router.get('/', (req: Request, res: Response) => {
41
+ const response: ApiResponse<HealthResponse> = {
42
+ success: true,
43
+ data: {
44
+ status: 'healthy',
45
+ version: process.env.npm_package_version || '0.1.0',
46
+ timestamp: new Date().toISOString(),
47
+ uptime: Math.floor((Date.now() - startTime) / 1000),
48
+ },
49
+ }
50
+
51
+ res.json(response)
52
+ })
53
+
54
+ export default router
@@ -0,0 +1,18 @@
1
+ import { Router } from 'express'
2
+ import healthRouter from './health'
3
+ import stealthRouter from './stealth'
4
+ import commitmentRouter from './commitment'
5
+ import proofRouter from './proof'
6
+ import swapRouter from './swap'
7
+
8
+ const router: Router = Router()
9
+
10
+ // Mount routes
11
+ router.use('/health', healthRouter)
12
+ router.use('/stealth', stealthRouter)
13
+ router.use('/commitment', commitmentRouter)
14
+ router.use('/proof', proofRouter)
15
+ router.use('/quote', swapRouter) // POST /quote
16
+ router.use('/swap', swapRouter) // POST /swap and GET /swap/:id/status
17
+
18
+ export default router
@@ -0,0 +1,82 @@
1
+ import { Router, Request, Response } from 'express'
2
+ import { MockProofProvider } from '@sip-protocol/sdk'
3
+ import { hexToBytes } from '@noble/hashes/utils'
4
+ import { validateRequest, schemas } from '../middleware'
5
+ import type { GenerateFundingProofRequest, FundingProofResponse, ApiResponse } from '../types/api'
6
+
7
+ const router: Router = Router()
8
+
9
+ // Initialize proof provider (use MockProofProvider for now)
10
+ // In production, use NoirProofProvider from '@sip-protocol/sdk/proofs/noir'
11
+ const proofProvider = new MockProofProvider()
12
+
13
+ /**
14
+ * POST /proof/funding
15
+ * Generate a funding proof (proves balance >= minimum without revealing exact balance)
16
+ *
17
+ * @openapi
18
+ * /proof/funding:
19
+ * post:
20
+ * summary: Generate funding proof
21
+ * description: Generate a zero-knowledge proof that balance meets minimum requirement
22
+ * requestBody:
23
+ * required: true
24
+ * content:
25
+ * application/json:
26
+ * schema:
27
+ * type: object
28
+ * required:
29
+ * - balance
30
+ * - minRequired
31
+ * - balanceBlinding
32
+ * properties:
33
+ * balance:
34
+ * type: string
35
+ * description: Actual balance (as string to handle bigint)
36
+ * example: "1000000000"
37
+ * minRequired:
38
+ * type: string
39
+ * description: Minimum required balance
40
+ * example: "500000000"
41
+ * balanceBlinding:
42
+ * type: string
43
+ * description: Blinding factor for balance commitment
44
+ * pattern: ^0x[0-9a-fA-F]+$
45
+ * responses:
46
+ * 200:
47
+ * description: Proof generated successfully
48
+ * 400:
49
+ * description: Invalid request parameters or balance too low
50
+ */
51
+ router.post(
52
+ '/funding',
53
+ validateRequest({ body: schemas.generateFundingProof }),
54
+ async (req: Request, res: Response) => {
55
+ const { balance, minRequired, balanceBlinding } = req.body as GenerateFundingProofRequest
56
+
57
+ const balanceBigInt = BigInt(balance)
58
+ const minRequiredBigInt = BigInt(minRequired)
59
+ const balanceBlindingBytes = hexToBytes(balanceBlinding)
60
+
61
+ const result = await proofProvider.generateFundingProof({
62
+ balance: balanceBigInt,
63
+ minimumRequired: minRequiredBigInt,
64
+ blindingFactor: balanceBlindingBytes,
65
+ assetId: 'SOL', // Default asset
66
+ userAddress: '0x0000000000000000000000000000000000000000',
67
+ ownershipSignature: new Uint8Array(64),
68
+ })
69
+
70
+ const response: ApiResponse<FundingProofResponse> = {
71
+ success: true,
72
+ data: {
73
+ proof: result.proof.proof,
74
+ publicInputs: result.proof.publicInputs,
75
+ },
76
+ }
77
+
78
+ res.json(response)
79
+ }
80
+ )
81
+
82
+ export default router
@@ -0,0 +1,76 @@
1
+ import { Router, Request, Response } from 'express'
2
+ import { generateStealthAddress } from '@sip-protocol/sdk'
3
+ import { validateRequest, schemas } from '../middleware'
4
+ import type { GenerateStealthRequest, StealthAddressResponse, ApiResponse } from '../types/api'
5
+
6
+ const router: Router = Router()
7
+
8
+ /**
9
+ * POST /stealth/generate
10
+ * Generate a stealth address for a recipient
11
+ *
12
+ * @openapi
13
+ * /stealth/generate:
14
+ * post:
15
+ * summary: Generate stealth address
16
+ * description: Generate a one-time stealth address for privacy-preserving transactions
17
+ * requestBody:
18
+ * required: true
19
+ * content:
20
+ * application/json:
21
+ * schema:
22
+ * type: object
23
+ * required:
24
+ * - chain
25
+ * - recipientMetaAddress
26
+ * properties:
27
+ * chain:
28
+ * type: string
29
+ * enum: [solana, ethereum, near, zcash, polygon, arbitrum, optimism, base, bitcoin]
30
+ * recipientMetaAddress:
31
+ * type: object
32
+ * required:
33
+ * - spendingKey
34
+ * - viewingKey
35
+ * - chain
36
+ * properties:
37
+ * spendingKey:
38
+ * type: string
39
+ * pattern: ^0x[0-9a-fA-F]+$
40
+ * viewingKey:
41
+ * type: string
42
+ * pattern: ^0x[0-9a-fA-F]+$
43
+ * chain:
44
+ * type: string
45
+ * label:
46
+ * type: string
47
+ * responses:
48
+ * 200:
49
+ * description: Stealth address generated successfully
50
+ * 400:
51
+ * description: Invalid request parameters
52
+ */
53
+ router.post(
54
+ '/generate',
55
+ validateRequest({ body: schemas.generateStealth }),
56
+ async (req: Request, res: Response) => {
57
+ const { chain, recipientMetaAddress } = req.body as GenerateStealthRequest
58
+
59
+ const result = generateStealthAddress(recipientMetaAddress)
60
+
61
+ const response: ApiResponse<StealthAddressResponse> = {
62
+ success: true,
63
+ data: {
64
+ stealthAddress: {
65
+ address: result.stealthAddress.address,
66
+ ephemeralPublicKey: result.stealthAddress.ephemeralPublicKey,
67
+ viewTag: result.stealthAddress.viewTag,
68
+ },
69
+ },
70
+ }
71
+
72
+ res.json(response)
73
+ }
74
+ )
75
+
76
+ export default router
@@ -0,0 +1,262 @@
1
+ import { Router, Request, Response } from 'express'
2
+ import { SIP, PrivacyLevel } from '@sip-protocol/sdk'
3
+ import { validateRequest, schemas } from '../middleware'
4
+ import type {
5
+ GetQuoteRequest,
6
+ ExecuteSwapRequest,
7
+ QuoteResponse,
8
+ SwapResponse,
9
+ SwapStatusResponse,
10
+ ApiResponse
11
+ } from '../types/api'
12
+
13
+ const router: Router = Router()
14
+
15
+ // Initialize SIP client
16
+ const sip = new SIP({ network: 'testnet' })
17
+
18
+ import type { HexString } from '@sip-protocol/types'
19
+
20
+ // In-memory swap tracking (in production, use a database)
21
+ const swaps = new Map<string, {
22
+ id: string
23
+ status: 'pending' | 'processing' | 'completed' | 'failed'
24
+ transactionHash?: HexString
25
+ inputAmount: string
26
+ outputAmount?: string
27
+ createdAt: string
28
+ updatedAt: string
29
+ error?: string
30
+ }>()
31
+
32
+ /**
33
+ * POST /quote
34
+ * Get a swap quote
35
+ *
36
+ * @openapi
37
+ * /quote:
38
+ * post:
39
+ * summary: Get swap quote
40
+ * description: Get a quote for a cross-chain swap
41
+ * requestBody:
42
+ * required: true
43
+ * content:
44
+ * application/json:
45
+ * schema:
46
+ * type: object
47
+ * required:
48
+ * - inputChain
49
+ * - inputToken
50
+ * - inputAmount
51
+ * - outputChain
52
+ * - outputToken
53
+ * properties:
54
+ * inputChain:
55
+ * type: string
56
+ * inputToken:
57
+ * type: string
58
+ * inputAmount:
59
+ * type: string
60
+ * outputChain:
61
+ * type: string
62
+ * outputToken:
63
+ * type: string
64
+ * slippageTolerance:
65
+ * type: number
66
+ * minimum: 0
67
+ * maximum: 100
68
+ * responses:
69
+ * 200:
70
+ * description: Quote retrieved successfully
71
+ */
72
+ router.post(
73
+ '/',
74
+ validateRequest({ body: schemas.getQuote }),
75
+ async (req: Request, res: Response) => {
76
+ const {
77
+ inputChain,
78
+ inputToken,
79
+ inputAmount,
80
+ outputChain,
81
+ outputToken,
82
+ slippageTolerance
83
+ } = req.body as GetQuoteRequest
84
+
85
+ // Create intent
86
+ const intent = await sip.createIntent({
87
+ input: {
88
+ asset: {
89
+ chain: inputChain,
90
+ address: null, // Native token
91
+ symbol: inputToken,
92
+ decimals: 9,
93
+ },
94
+ amount: BigInt(inputAmount),
95
+ },
96
+ output: {
97
+ asset: {
98
+ chain: outputChain,
99
+ address: null, // Native token
100
+ symbol: outputToken,
101
+ decimals: 9,
102
+ },
103
+ minAmount: BigInt(inputAmount) * 95n / 100n, // 5% slippage
104
+ maxSlippage: (slippageTolerance || 1) / 100,
105
+ },
106
+ privacy: PrivacyLevel.TRANSPARENT, // Default to transparent for quote
107
+ })
108
+
109
+ // Get quotes (using mock data for now)
110
+ // In production, this would query real DEX aggregators
111
+ const mockQuote: QuoteResponse = {
112
+ quoteId: `quote-${Date.now()}`,
113
+ inputAmount,
114
+ outputAmount: (BigInt(inputAmount) * 95n / 100n).toString(), // Mock 5% fee
115
+ rate: '0.95',
116
+ estimatedTime: 30,
117
+ fees: {
118
+ network: '0.001',
119
+ protocol: '0.003',
120
+ },
121
+ route: {
122
+ steps: [
123
+ {
124
+ chain: inputChain,
125
+ protocol: 'NEAR Intents',
126
+ fromToken: inputToken,
127
+ toToken: outputToken,
128
+ },
129
+ ],
130
+ },
131
+ }
132
+
133
+ const response: ApiResponse<QuoteResponse> = {
134
+ success: true,
135
+ data: mockQuote,
136
+ }
137
+
138
+ res.json(response)
139
+ }
140
+ )
141
+
142
+ /**
143
+ * POST /swap
144
+ * Execute a swap
145
+ *
146
+ * @openapi
147
+ * /swap:
148
+ * post:
149
+ * summary: Execute swap
150
+ * description: Execute a cross-chain swap with optional privacy
151
+ * requestBody:
152
+ * required: true
153
+ * content:
154
+ * application/json:
155
+ * schema:
156
+ * type: object
157
+ * required:
158
+ * - intentId
159
+ * - quoteId
160
+ * properties:
161
+ * intentId:
162
+ * type: string
163
+ * quoteId:
164
+ * type: string
165
+ * privacy:
166
+ * type: string
167
+ * enum: [transparent, shielded, compliant]
168
+ * viewingKey:
169
+ * type: string
170
+ * pattern: ^0x[0-9a-fA-F]+$
171
+ * responses:
172
+ * 200:
173
+ * description: Swap initiated successfully
174
+ */
175
+ router.post(
176
+ '/swap',
177
+ validateRequest({ body: schemas.executeSwap }),
178
+ async (req: Request, res: Response) => {
179
+ const { intentId, quoteId, privacy, viewingKey } = req.body as ExecuteSwapRequest
180
+
181
+ // Generate swap ID
182
+ const swapId = `swap-${Date.now()}`
183
+
184
+ // Store swap status
185
+ const swap = {
186
+ id: swapId,
187
+ status: 'pending' as const,
188
+ inputAmount: '1000000000', // Mock value
189
+ createdAt: new Date().toISOString(),
190
+ updatedAt: new Date().toISOString(),
191
+ }
192
+ swaps.set(swapId, swap)
193
+
194
+ // In production, this would:
195
+ // 1. Sign the transaction
196
+ // 2. Submit to the network
197
+ // 3. Track the transaction
198
+ // For now, we just return a mock response
199
+
200
+ const response: ApiResponse<SwapResponse> = {
201
+ success: true,
202
+ data: {
203
+ swapId,
204
+ status: 'pending',
205
+ timestamp: new Date().toISOString(),
206
+ },
207
+ }
208
+
209
+ res.json(response)
210
+ }
211
+ )
212
+
213
+ /**
214
+ * GET /swap/:id/status
215
+ * Get swap status
216
+ *
217
+ * @openapi
218
+ * /swap/{id}/status:
219
+ * get:
220
+ * summary: Get swap status
221
+ * description: Check the status of a swap transaction
222
+ * parameters:
223
+ * - in: path
224
+ * name: id
225
+ * required: true
226
+ * schema:
227
+ * type: string
228
+ * description: Swap ID
229
+ * responses:
230
+ * 200:
231
+ * description: Swap status retrieved
232
+ * 404:
233
+ * description: Swap not found
234
+ */
235
+ router.get(
236
+ '/:id/status',
237
+ validateRequest({ params: schemas.swapStatus }),
238
+ async (req: Request, res: Response) => {
239
+ const { id } = req.params
240
+
241
+ const swap = swaps.get(id)
242
+
243
+ if (!swap) {
244
+ return res.status(404).json({
245
+ success: false,
246
+ error: {
247
+ code: 'SWAP_NOT_FOUND',
248
+ message: `Swap ${id} not found`,
249
+ },
250
+ })
251
+ }
252
+
253
+ const response: ApiResponse<SwapStatusResponse> = {
254
+ success: true,
255
+ data: swap,
256
+ }
257
+
258
+ res.json(response)
259
+ }
260
+ )
261
+
262
+ export default router
package/src/server.ts ADDED
@@ -0,0 +1,76 @@
1
+ import express, { Express } from 'express'
2
+ import cors from 'cors'
3
+ import helmet from 'helmet'
4
+ import compression from 'compression'
5
+ import morgan from 'morgan'
6
+ import router from './routes'
7
+ import { errorHandler, notFoundHandler } from './middleware'
8
+
9
+ const app: Express = express()
10
+
11
+ // Environment configuration
12
+ const PORT = process.env.PORT || 3000
13
+ const NODE_ENV = process.env.NODE_ENV || 'development'
14
+ const CORS_ORIGIN = process.env.CORS_ORIGIN || '*'
15
+
16
+ // Security middleware
17
+ app.use(helmet())
18
+ app.use(cors({
19
+ origin: CORS_ORIGIN,
20
+ credentials: true,
21
+ }))
22
+
23
+ // Body parsing middleware
24
+ app.use(express.json({ limit: '1mb' }))
25
+ app.use(express.urlencoded({ extended: true, limit: '1mb' }))
26
+
27
+ // Compression middleware
28
+ app.use(compression())
29
+
30
+ // Logging middleware
31
+ app.use(morgan(NODE_ENV === 'development' ? 'dev' : 'combined'))
32
+
33
+ // API routes
34
+ app.use('/api/v1', router)
35
+
36
+ // Root endpoint
37
+ app.get('/', (req, res) => {
38
+ res.json({
39
+ name: '@sip-protocol/api',
40
+ version: '0.1.0',
41
+ description: 'REST API service for SIP Protocol SDK',
42
+ documentation: '/api/v1/health',
43
+ endpoints: {
44
+ health: 'GET /api/v1/health',
45
+ stealth: 'POST /api/v1/stealth/generate',
46
+ commitment: 'POST /api/v1/commitment/create',
47
+ proof: 'POST /api/v1/proof/funding',
48
+ quote: 'POST /api/v1/quote',
49
+ swap: 'POST /api/v1/swap',
50
+ swapStatus: 'GET /api/v1/swap/:id/status',
51
+ },
52
+ })
53
+ })
54
+
55
+ // 404 handler
56
+ app.use(notFoundHandler)
57
+
58
+ // Error handler (must be last)
59
+ app.use(errorHandler)
60
+
61
+ // Start server
62
+ if (require.main === module) {
63
+ app.listen(PORT, () => {
64
+ console.log(`
65
+ ╔════════════════════════════════════════════════════╗
66
+ ║ SIP Protocol REST API ║
67
+ ║ Version: 0.1.0 ║
68
+ ║ Port: ${PORT} ║
69
+ ║ Environment: ${NODE_ENV} ║
70
+ ║ Documentation: http://localhost:${PORT}/ ║
71
+ ╚════════════════════════════════════════════════════╝
72
+ `)
73
+ })
74
+ }
75
+
76
+ export default app