Skip to content

Commit 538a4ec

Browse files
authored
Merge pull request #163 from MeshJS/feature/api-transaction-fetching-and-signing
Add rawImportBodies field to Wallet model in schema.prisma
2 parents 4a5ec31 + 8eaccba commit 538a4ec

File tree

5 files changed

+396
-19
lines changed

5 files changed

+396
-19
lines changed

prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ model Wallet {
3131
type String
3232
isArchived Boolean @default(false)
3333
clarityApiKey String?
34+
rawImportBodies Json?
3435
migrationTargetWalletId String?
3536
}
3637

src/pages/api/v1/README.md

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ A comprehensive REST API implementation for the multisig wallet application, pro
55
## Authentication & Security
66

77
### JWT-Based Authentication
8+
89
- **Bearer Token Authentication**: All endpoints require valid JWT tokens
910
- **Address-Based Authorization**: Token payload contains user address for authorization
1011
- **Session Management**: 1-hour token expiration with automatic renewal
1112
- **CORS Support**: Cross-origin resource sharing enabled for web clients
1213
- **Address Validation**: Strict address matching between token and request parameters
1314

1415
### Security Features
16+
1517
- **Input Validation**: Comprehensive parameter validation and sanitization
1618
- **Error Handling**: Detailed error responses without sensitive information exposure
1719
- **Rate Limiting**: Built-in protection against abuse (via CORS and validation)
@@ -22,6 +24,7 @@ A comprehensive REST API implementation for the multisig wallet application, pro
2224
### Transaction Management
2325

2426
#### `addTransaction.ts` - POST `/api/v1/addTransaction`
27+
2528
- **Purpose**: Submit external transactions for multisig wallet processing
2629
- **Authentication**: Required (JWT Bearer token)
2730
- **Features**:
@@ -40,7 +43,24 @@ A comprehensive REST API implementation for the multisig wallet application, pro
4043
- **Response**: Transaction object with ID, state, and metadata
4144
- **Error Handling**: 400 (validation), 401 (auth), 403 (authorization), 500 (server)
4245

46+
#### `signTransaction.ts` - POST `/api/v1/signTransaction`
47+
48+
- **Purpose**: Record a signature for a pending multisig transaction
49+
- **Authentication**: Required (JWT Bearer token)
50+
- **Features**:
51+
- Signature tracking with duplicate and rejection safeguards
52+
- Wallet membership validation and JWT address enforcement
53+
- Threshold detection with automatic submission when the final signature is collected
54+
- **Request Body**:
55+
- `walletId`: Wallet identifier
56+
- `transactionId`: Pending transaction identifier
57+
- `address`: Signer address
58+
- `signedTx`: CBOR transaction payload after applying the signature
59+
- **Response**: Updated transaction record with threshold status metadata; includes `txHash` when submission succeeds
60+
- **Error Handling**: 400 (validation), 401 (auth), 403 (authorization), 404 (not found), 409 (duplicate/rejected), 502 (submission failure), 500 (server)
61+
4362
#### `submitDatum.ts` - POST `/api/v1/submitDatum`
63+
4464
- **Purpose**: Submit signable payloads for multisig signature collection
4565
- **Authentication**: Required (JWT Bearer token)
4666
- **Features**:
@@ -63,6 +83,7 @@ A comprehensive REST API implementation for the multisig wallet application, pro
6383
### Wallet Management
6484

6585
#### `walletIds.ts` - GET `/api/v1/walletIds`
86+
6687
- **Purpose**: Retrieve all wallet IDs and names for a user address
6788
- **Authentication**: Required (JWT Bearer token)
6889
- **Features**:
@@ -76,6 +97,7 @@ A comprehensive REST API implementation for the multisig wallet application, pro
7697
- **Error Handling**: 400 (validation), 401 (auth), 403 (authorization), 404 (not found), 500 (server)
7798

7899
#### `nativeScript.ts` - GET `/api/v1/nativeScript`
100+
79101
- **Purpose**: Generate native scripts for multisig wallet operations
80102
- **Authentication**: Required (JWT Bearer token)
81103
- **Features**:
@@ -90,6 +112,7 @@ A comprehensive REST API implementation for the multisig wallet application, pro
90112
- **Error Handling**: 400 (validation), 401 (auth), 403 (authorization), 404 (not found), 500 (server)
91113

92114
#### `lookupMultisigWallet.ts` - GET `/api/v1/lookupMultisigWallet`
115+
93116
- **Purpose**: Lookup multisig wallet metadata using public key hashes
94117
- **Authentication**: Not required (public endpoint)
95118
- **Features**:
@@ -106,6 +129,7 @@ A comprehensive REST API implementation for the multisig wallet application, pro
106129
### UTxO Management
107130

108131
#### `freeUtxos.ts` - GET `/api/v1/freeUtxos`
132+
109133
- **Purpose**: Retrieve unblocked UTxOs for a multisig wallet
110134
- **Authentication**: Required (JWT Bearer token)
111135
- **Features**:
@@ -123,6 +147,7 @@ A comprehensive REST API implementation for the multisig wallet application, pro
123147
### Authentication Endpoints
124148

125149
#### `getNonce.ts` - GET `/api/v1/getNonce`
150+
126151
- **Purpose**: Request authentication nonce for address-based signing
127152
- **Authentication**: Not required (public endpoint)
128153
- **Features**:
@@ -136,6 +161,7 @@ A comprehensive REST API implementation for the multisig wallet application, pro
136161
- **Error Handling**: 400 (validation), 404 (user not found), 500 (server)
137162

138163
#### `authSigner.ts` - POST `/api/v1/authSigner`
164+
139165
- **Purpose**: Verify signed nonce and return JWT bearer token
140166
- **Authentication**: Not required (public endpoint)
141167
- **Features**:
@@ -153,6 +179,7 @@ A comprehensive REST API implementation for the multisig wallet application, pro
153179
### Utility Endpoints
154180

155181
#### `og.ts` - GET `/api/v1/og`
182+
156183
- **Purpose**: Extract Open Graph metadata from URLs
157184
- **Authentication**: Not required (public endpoint)
158185
- **Features**:
@@ -168,25 +195,29 @@ A comprehensive REST API implementation for the multisig wallet application, pro
168195
## API Architecture
169196

170197
### Request/Response Patterns
198+
171199
- **RESTful Design**: Standard HTTP methods and status codes
172200
- **JSON Format**: All requests and responses use JSON
173201
- **Error Consistency**: Standardized error response format
174202
- **CORS Support**: Cross-origin requests enabled
175203

176204
### Authentication Flow
205+
177206
1. **Nonce Request**: Client requests nonce for address
178207
2. **Signature Generation**: Client signs nonce with private key
179208
3. **Token Exchange**: Client exchanges signature for JWT token
180209
4. **API Access**: Client uses JWT token for authenticated requests
181210

182211
### Error Handling
212+
183213
- **HTTP Status Codes**: Proper status code usage
184214
- **Error Messages**: Descriptive error messages
185215
- **Validation Errors**: Detailed parameter validation
186216
- **Security Errors**: Authentication and authorization failures
187217
- **Server Errors**: Internal server error handling
188218

189219
### Database Integration
220+
190221
- **Prisma ORM**: Type-safe database operations
191222
- **Transaction Management**: Database transaction handling
192223
- **Data Validation**: Input validation and sanitization
@@ -195,18 +226,21 @@ A comprehensive REST API implementation for the multisig wallet application, pro
195226
## Security Considerations
196227

197228
### Input Validation
229+
198230
- **Parameter Validation**: All input parameters validated
199231
- **Type Checking**: Strict type validation for all inputs
200232
- **Address Validation**: Cardano address format validation
201233
- **Signature Verification**: Cryptographic signature validation
202234

203235
### Authorization
236+
204237
- **Address Matching**: Token address must match request address
205238
- **Wallet Access**: Users can only access their own wallets
206239
- **Resource Protection**: Sensitive operations require authentication
207240
- **Session Management**: Token expiration and renewal
208241

209242
### Data Protection
243+
210244
- **Sensitive Data**: No sensitive data in error messages
211245
- **Logging**: Comprehensive logging without sensitive information
212246
- **CORS**: Proper cross-origin resource sharing configuration
@@ -215,71 +249,81 @@ A comprehensive REST API implementation for the multisig wallet application, pro
215249
## Dependencies
216250

217251
### Core Dependencies
252+
218253
- **Next.js API Routes**: Server-side API implementation
219254
- **Prisma**: Database ORM and query builder
220255
- **jsonwebtoken**: JWT token generation and verification
221256
- **@meshsdk/core**: Cardano blockchain interactions
222257
- **@meshsdk/core-cst**: Cryptographic signature verification
223258

224259
### Utility Dependencies
260+
225261
- **crypto**: Node.js cryptographic functions
226262
- **cors**: Cross-origin resource sharing
227263
- **fetch**: HTTP client for external requests
228264

229265
## Environment Variables
230266

231267
### Required Variables
268+
232269
- `JWT_SECRET`: Secret key for JWT token generation
233270
- `NEXT_PUBLIC_BLOCKFROST_API_KEY_PREPROD`: Preprod network API key
234271
- `NEXT_PUBLIC_BLOCKFROST_API_KEY_MAINNET`: Mainnet network API key
235272

236273
### Database Configuration
274+
237275
- Database connection via Prisma configuration
238276
- Environment-specific database URLs
239277
- Connection pooling and optimization
240278

241279
## Usage Examples
242280

243281
### Authentication Flow
282+
244283
```typescript
245284
// 1. Request nonce
246-
const nonceResponse = await fetch('/api/v1/getNonce?address=addr1...');
285+
const nonceResponse = await fetch("/api/v1/getNonce?address=addr1...");
247286
const { nonce } = await nonceResponse.json();
248287

249288
// 2. Sign nonce and get token
250289
const signature = await wallet.signData(nonce, address);
251-
const tokenResponse = await fetch('/api/v1/authSigner', {
252-
method: 'POST',
253-
body: JSON.stringify({ address, signature, key: publicKey })
290+
const tokenResponse = await fetch("/api/v1/authSigner", {
291+
method: "POST",
292+
body: JSON.stringify({ address, signature, key: publicKey }),
254293
});
255294
const { token } = await tokenResponse.json();
256295
```
257296

258297
### Transaction Submission
298+
259299
```typescript
260-
const response = await fetch('/api/v1/addTransaction', {
261-
method: 'POST',
300+
const response = await fetch("/api/v1/addTransaction", {
301+
method: "POST",
262302
headers: {
263-
'Authorization': `Bearer ${token}`,
264-
'Content-Type': 'application/json'
303+
Authorization: `Bearer ${token}`,
304+
"Content-Type": "application/json",
265305
},
266306
body: JSON.stringify({
267-
walletId: 'wallet-id',
268-
address: 'addr1...',
269-
txCbor: 'tx-cbor-data',
270-
txJson: 'tx-json-data',
271-
description: 'Transaction description'
272-
})
307+
walletId: "wallet-id",
308+
address: "addr1...",
309+
txCbor: "tx-cbor-data",
310+
txJson: "tx-json-data",
311+
description: "Transaction description",
312+
}),
273313
});
274314
```
275315

276316
### UTxO Retrieval
317+
277318
```typescript
278-
const response = await fetch('/api/v1/freeUtxos?walletId=wallet-id&address=addr1...', {
279-
headers: {
280-
'Authorization': `Bearer ${token}`
281-
}
282-
});
319+
const response = await fetch(
320+
"/api/v1/freeUtxos?walletId=wallet-id&address=addr1...",
321+
{
322+
headers: {
323+
Authorization: `Bearer ${token}`,
324+
},
325+
},
326+
);
283327
const freeUtxos = await response.json();
284328
```
285329

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { cors, addCorsCacheBustingHeaders } from "@/lib/cors";
2+
import { verifyJwt } from "@/lib/verifyJwt";
3+
import { createCaller } from "@/server/api/root";
4+
import { db } from "@/server/db";
5+
import type { NextApiRequest, NextApiResponse } from "next";
6+
7+
export default async function handler(
8+
req: NextApiRequest,
9+
res: NextApiResponse,
10+
) {
11+
// Add cache-busting headers for CORS
12+
addCorsCacheBustingHeaders(res);
13+
14+
await cors(req, res);
15+
if (req.method === "OPTIONS") {
16+
return res.status(200).end();
17+
}
18+
19+
if (req.method !== "GET") {
20+
return res.status(405).json({ error: "Method Not Allowed" });
21+
}
22+
23+
const authHeader = req.headers.authorization;
24+
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
25+
26+
if (!token) {
27+
return res.status(401).json({ error: "Unauthorized - Missing token" });
28+
}
29+
30+
const payload = verifyJwt(token);
31+
if (!payload) {
32+
return res.status(401).json({ error: "Invalid or expired token" });
33+
}
34+
35+
const session = {
36+
user: { id: payload.address },
37+
expires: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
38+
} as const;
39+
40+
const { walletId, address } = req.query;
41+
42+
if (typeof address !== "string") {
43+
return res.status(400).json({ error: "Invalid address parameter" });
44+
}
45+
if (payload.address !== address) {
46+
return res.status(403).json({ error: "Address mismatch" });
47+
}
48+
if (typeof walletId !== "string") {
49+
return res.status(400).json({ error: "Invalid walletId parameter" });
50+
}
51+
52+
try {
53+
const caller = createCaller({ db, session });
54+
55+
const wallet = await caller.wallet.getWallet({ walletId, address });
56+
if (!wallet) {
57+
return res.status(404).json({ error: "Wallet not found" });
58+
}
59+
60+
const pendingTransactions =
61+
await caller.transaction.getPendingTransactions({ walletId });
62+
63+
return res.status(200).json(pendingTransactions);
64+
} catch (error) {
65+
console.error("Error in pendingTransactions handler", {
66+
message: (error as Error)?.message,
67+
stack: (error as Error)?.stack,
68+
});
69+
return res.status(500).json({ error: "Internal Server Error" });
70+
}
71+
}
72+

0 commit comments

Comments
 (0)