@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
|
@@ -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
|