Skip to content

Commit baddd3c

Browse files
authored
Merge pull request #23 from solana-developers/new-transaction-checks
adding new validation endpoint
2 parents 43e5e18 + de71fd5 commit baddd3c

File tree

6 files changed

+260
-84
lines changed

6 files changed

+260
-84
lines changed

README.md

Lines changed: 123 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -113,134 +113,185 @@ Below are the available endpoints for each table.
113113

114114
---
115115

116-
## Rate Limits Endpoints
116+
## Github Validation Endpoints
117+
118+
### **Validate Github User ID**
119+
120+
**GET** `/api/github-validation/:userId`
121+
122+
- **Description**: Validates a Github user by fetching their information from the Github API using their user ID.
123+
- **Request Params**:
124+
- `userId` (string): The Github User ID to validate.
125+
126+
- **Curl Command**:
127+
```bash
128+
curl -v http://localhost:3000/api/gh-validation/exampleUser
129+
```
130+
-**Response**:
131+
```json
132+
{
133+
"valid": "boolean"
134+
}
135+
```
136+
137+
---
138+
139+
## Transactions Endpoints
117140

118-
### **Create a New Rate Limit**
141+
### **Create a New Transaction**
119142

120-
**POST** `/api/rate-limits`
143+
**POST** `/api/transactions`
121144

122-
- **Description**: Adds a new rate limit entry.
145+
- **Description**: Creates a new transaction entry with a unique signature.
123146
- **Request Body**:
124147
```json
125148
{
126-
"key": "string",
127-
"timestamps": ["number"]
149+
"signature": "string",
150+
"ip_address": "string",
151+
"wallet_address": "string",
152+
"github_id": "string (optional)",
153+
"timestamp": "number"
128154
}
129155
```
130156
- **Curl Command**:
131157
```bash
132-
curl -v -X POST http://localhost:3000/api/rate-limits \
133-
-H "Content-Type: application/json" \
134-
-d '{"key": "test_key_1", "timestamps": [1635793421]}'
158+
curl -v -X POST http://localhost:3000/api/transactions \
159+
-H "Content-Type: application/json" \
160+
-d '{
161+
"signature": "tx_123",
162+
"ip_address": "192.168.0.1",
163+
"wallet_address": "wallet_abc",
164+
"github_id": "user123",
165+
"timestamp": 1714752000
166+
}'
135167
```
136168
- **Response**:
137169
```json
138170
{
139-
"key": "string",
140-
"timestamps": ["number"]
171+
"signature": "tx_123",
172+
"ip_address": "192.168.0.1",
173+
"wallet_address": "wallet_abc",
174+
"github_id": "user123",
175+
"timestamp": 1714752000
141176
}
142177
```
143178

144-
### **Get a Rate Limit by Key**
179+
---
180+
181+
### **Get the Most Recent Transaction(s)**
182+
183+
**GET** `/api/transactions/last`
145184

146-
**GET** `/api/rate-limits/:key`
185+
- **Description**: Retrieves the most recent transaction(s) matching the given query parameters. You must provide at least one of `wallet_address` or `ip_address`.
186+
- **Query Params**:
187+
- `wallet_address` (string, optional)
188+
- `github_id` (string, optional)
189+
- `ip_address` (string, optional)
190+
- `count` (number, optional – number of results to return; defaults to 1)
147191

148-
- **Description**: Retrieves the rate limit entry for a specific key.
149192
- **Curl Command**:
150193
```bash
151-
curl -v http://localhost:3000/api/rate-limits/test_key_1
194+
curl -v "http://localhost:3000/api/transactions/last?wallet_address=wallet_abc&count=2"
152195
```
153-
- **Response**:
196+
- **Response** (if found):
197+
```json
198+
[
199+
{
200+
"signature": "tx_123",
201+
"ip_address": "192.168.0.1",
202+
"wallet_address": "wallet_abc",
203+
"github_id": "user123",
204+
"timestamp": 1714752000
205+
}
206+
]
207+
```
208+
209+
- **Response** (if not found):
154210
```json
155211
{
156-
"key": "string",
157-
"timestamps": ["number"]
212+
"message": "No transaction found for the given criteria."
158213
}
159214
```
160215

161-
### **Update Timestamps for a Rate Limit**
216+
---
162217

163-
**PUT** `/api/rate-limits/:key`
218+
### **Delete a Transaction by Signature**
164219

165-
- **Description**: Updates the timestamps for a specific rate limit key.
166-
- **Request Body**:
220+
**DELETE** `/api/transactions/:signature`
221+
222+
- **Description**: Deletes a transaction based on its signature.
223+
- **Curl Command**:
224+
```bash
225+
curl -v -X DELETE http://localhost:3000/api/transactions/tx_123
226+
```
227+
- **Response** (if deleted):
167228
```json
168229
{
169-
"timestamps": ["number"]
230+
"signature": "tx_123",
231+
"ip_address": "192.168.0.1",
232+
"wallet_address": "wallet_abc",
233+
"github_id": "user123",
234+
"timestamp": 1714752000
170235
}
171236
```
172-
- **Curl Command**:
173-
```bash
174-
curl -v -X PUT http://localhost:3000/api/rate-limits/test_key_1 \
175-
-H "Content-Type: application/json" \
176-
-d '{"timestamps": [1635793500]}'
177-
```
178-
- **Response**:
237+
238+
- **Response** (if not found):
179239
```json
180240
{
181-
"key": "string",
182-
"timestamps": ["number"]
241+
"message": "Transaction not found"
183242
}
184243
```
185244

186-
### **Create a New Rate Limit Combination**
245+
---
246+
247+
### **Validate User Information**
248+
249+
**POST** `/api/validate`
250+
251+
- **Description**: Validates a GitHub account and checks the transaction history for the given IP address, wallet address, and GitHub ID.
187252

188-
**POST** `/api/rate-limits-combo`
253+
#### Validation Criteria:
254+
- **GitHub Account**
255+
- Must be at least 30 days old
256+
- Must have at least 1 public repository
257+
- Must be of type `User`
258+
259+
- **Transaction Limits**
260+
- Max 200 transactions per IP address (all-time)
261+
- Max 100 transactions per wallet address (all-time)
262+
- Max 100 transactions per GitHub ID (all-time)
263+
- Max 50 transactions for the combination of all three within the last 30 days
189264

190-
- **Description**: Adds a new rate limit combination entry. Each combination of `ip_address`, `wallet_address`,
191-
and `github_userid` is checked for uniqueness before inserting to DB.
192265
- **Request Body**:
193266
```json
194267
{
195268
"ip_address": "string",
196269
"wallet_address": "string",
197-
"github_userid": "string"
270+
"github_id": "string"
198271
}
199272
```
273+
200274
- **Curl Command**:
201275
```bash
202-
curl -v -X POST http://localhost:3000/api/rate-limits-combo \
203-
-H "Content-Type: application/json" \
204-
-d '{
205-
"ip_address": "19216801",
206-
"wallet_address": "wallet_123",
207-
"github_userid": "user123"
208-
}'
276+
curl -v -X POST http://localhost:3000/api/validate -H "Content-Type: application/json" -d '{"ip_address": "1234567", "wallet_address": "some_address", "github_id": "54321"}'
209277
```
210-
- **Response**:
211-
```json
278+
279+
- **Response (Valid)**:
280+
```json
212281
{
213-
"id": "3",
214-
"ip_address":"19216801",
215-
"wallet_address":"wallet_123",
216-
"github_userid":"user123"
282+
"valid": true,
283+
"reason": ""
217284
}
218285
```
219286

220-
---
221-
222-
## Github Validation Endpoints
223-
224-
### **Validate Github User ID**
225-
226-
**GET** `/api/github-validation/:userId`
227-
228-
- **Description**: Validates a Github user by fetching their information from the Github API using their user ID.
229-
- **Request Params**:
230-
- `userId` (string): The Github User ID to validate.
231-
232-
- **Curl Command**:
233-
```bash
234-
curl -v http://localhost:3000/api/gh-validation/exampleUser
287+
- **Response (Invalid)**:
288+
```json
289+
{
290+
"valid": false,
291+
"reason": "Transaction history is invalid"
292+
}
235293
```
236-
-**Response**:
237-
```json
238-
{
239-
"valid": "boolean"
240-
}
241-
```
242294

243-
---
244295

245296
## Error Handling
246297

src/clients/githubClient.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,17 @@ class GithubClient {
2828
return await client.request(endpoint, params);
2929
} catch (err) {
3030
lastError = err;
31-
// If the error is due to rate limiting, rotate the token
31+
3232
if (err.status === 403) {
33+
// If the error is due to rate limiting, rotate the token
3334
console.warn(`Token at index ${this.index} failed with status 403. Rotating token...`);
3435
this.rotateToken();
3536
attempts++;
37+
} else if (err.status === 404) {
38+
throw {
39+
status: 404,
40+
message: "GitHub user not found"
41+
};
3642
} else {
3743
throw err;
3844
}

src/db/transactions.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const createTransaction = async (signature, ip_address, wallet_address, github_i
1111
return result.rows[0];
1212
};
1313

14-
const getLastTransaction = async ({ wallet_address, github_id, ip_address, queryLimit }) => {
14+
const getLastTransaction = async ({wallet_address, github_id, ip_address, queryLimit}) => {
1515
let query;
1616
let values;
1717

@@ -54,8 +54,44 @@ const deleteTransaction = async (signature) => {
5454
return result.rows[0];
5555
};
5656

57+
// Get count for IP, wallet, and GitHub ID (all-time)
58+
const getTransactionStats = async ({ip_address, wallet_address, github_id}) => {
59+
const query = `
60+
SELECT
61+
COUNT(*) FILTER (WHERE ip_address = $1) AS ip_count,
62+
COUNT(*) FILTER (WHERE wallet_address = $2) AS wallet_count,
63+
COUNT(*) FILTER (WHERE github_id = $3) AS github_count
64+
FROM faucet.transactions
65+
WHERE
66+
ip_address = $1 OR
67+
wallet_address = $2 OR
68+
github_id = $3;
69+
`;
70+
const values = [ip_address, wallet_address, github_id];
71+
const result = await db.query(query, values);
72+
return result.rows[0];
73+
};
74+
75+
// Get count of combo IP + Wallet + GitHub (last 30 days)
76+
const getMonthlyTransactionStats = async ({ip_address, wallet_address, github_id}) => {
77+
const query = `
78+
SELECT COUNT(*) AS combo_count
79+
FROM faucet.transactions
80+
WHERE
81+
ip_address = $1 AND
82+
wallet_address = $2 AND
83+
github_id = $3 AND
84+
timestamp >= EXTRACT(EPOCH FROM NOW() - INTERVAL '30 days');
85+
`;
86+
const values = [ip_address, wallet_address, github_id];
87+
const result = await db.query(query, values);
88+
return Number(result.rows[0]?.combo_count || 0);
89+
};
90+
5791
export default {
5892
createTransaction,
5993
getLastTransaction,
60-
deleteTransaction
94+
deleteTransaction,
95+
getTransactionStats,
96+
getMonthlyTransactionStats
6197
};

src/routes/githubValidationRoute.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,6 @@ router.get("/gh-validation/:userId", async (req, res) => {
4242
res.status(200).json({ valid });
4343
} catch (error) {
4444
console.error("Error calling GitHub API:", error);
45-
46-
// Handle 404 error when user is not found
47-
if (error.response?.status === 404) {
48-
return res.status(404).json({
49-
error: "GitHub user not found",
50-
valid: false,
51-
});
52-
}
53-
5445
res.status(500).json({ error: "Internal server error." });
5546
}
5647
});

src/routes/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import express from 'express';
22
import rateLimitRoute from './rateLimitRoute.js';
33
import transactionsRoute from './transactionsRoute.js';
4+
import transactionValidationRoute from './transactionValidationRoute.js';
45
import solanaBalancesRoute from "./solanaBalancesRoute.js";
56
import githubValidationRoute from "./githubValidationRoute.js";
67

@@ -12,6 +13,9 @@ router.use(rateLimitRoute);
1213
// Use transactions routes
1314
router.use(transactionsRoute);
1415

16+
// Use transactions validation routes
17+
router.use(transactionValidationRoute);
18+
1519
// Use Solana balances routes
1620
router.use(solanaBalancesRoute);
1721

0 commit comments

Comments
 (0)