Skip to content

Commit b5ded62

Browse files
authored
Added ws connection to retrieve status details on requestId (#89)
* Added ws connection to retrieve status details on requestId * Completed the Loop also added tx mined status confirmation action * updated the status code to 200 * updated implementation to send request data on connection & close if tx mined or errored * Added Pool Config. Added DB decorators. Using best practices for postgres * adjusted the cron schedule to include seconds * updated DB connect-destroy usage * updates for e2e & cron destroy before return * updated code to destroy DB connection post usage * updated cron to 30 seconds * removed process.on SIGTERM listerner * removed commented triggers, types folder for fastify * updated messaging * added formatSocketMessage() * updates for query. and cleanup * updates for logs & removed pooling. let db handle * added websockets setup guide * updates * added response example for websockets
1 parent 38f2691 commit b5ded62

File tree

27 files changed

+882
-178
lines changed

27 files changed

+882
-178
lines changed

.github/websocket_usage.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
## How to use websockets
2+
3+
For listening for updates on your requests, you can either poll using the `get` method or use websockets. This guide will help in setting up websockets.
4+
5+
### Requirements
6+
7+
- Chrome (or any explorer)
8+
9+
### Steps
10+
11+
1. Open Chrome browser
12+
2. Open Chrome DevTools (F12)
13+
3. Replace the below values with your own values in the below snippet and paste it in the console
14+
- `<host>`: Hostname of the server
15+
- `<port>`: Port of the server
16+
- `<queueId>`: Queue ID of the request
17+
- `<w3a_thirdweb_secret_key>`: Web3-API Thirdweb secret key
18+
19+
```js
20+
const socket = new WebSocket(
21+
"ws://<host>:<port>/transaction/status/<queueId>?token=<w3a_thirdweb_secret_key>",
22+
);
23+
24+
socket.onopen = (event) => {
25+
console.log("opened");
26+
};
27+
28+
socket.onclose = (event) => {
29+
console.log("connection closed");
30+
};
31+
32+
socket.onmessage = (event) => {
33+
const res = JSON.parse(event.data);
34+
res.result = JSON.parse(res.result);
35+
console.log("Received Data", res);
36+
};
37+
```
38+
39+
Received Data will be of the following format:
40+
41+
```json
42+
{
43+
"message": string,
44+
"queueId": string,
45+
"result": {
46+
"chainId": string,
47+
"contractAddress": string,
48+
"contractType": string | null,
49+
"createdTimestamp": datetime,
50+
"deployedContractAddress": string | null,
51+
"encodedInputData": string,
52+
"errorMessage": string | null,
53+
"extension": string,
54+
"functionArgs": string,
55+
"functionName": string,
56+
"gasLimit": string | null,
57+
"gasPrice": string | null,
58+
"maxFeePerGas": string | null,
59+
"maxPriorityFeePerGas": string | null,
60+
"queueId": string | null,
61+
"status": string | null,
62+
"submittedTxNonce": string | null,
63+
"toAddress": string | null,
64+
"txErrored": boolean,
65+
"txHash": string,
66+
"txMined": boolean,
67+
"txProcessed": boolean,
68+
"txProcessedTimestamp": datetime | null,
69+
"txRetryTimestamp": datetime | null,
70+
"txSubmitted": boolean,
71+
"txSubmittedTimestamp": datetime | null,
72+
"txType": string | null,
73+
"updatedTimestamp": datetime | null,
74+
"walletAddress": string
75+
}
76+
}
77+
```
78+
79+
The console will start logging the updates on the queueId as and when they are trasmitted.
80+
81+
The websocket connection will be closed automatically after the last update of either `errored` or `mined` status is sent.
82+
83+
#### Note: `wss` can be used instead of `ws` for secure connections.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,4 @@ swagger.yml
134134
worker/distWorker/
135135
.DS_Store
136136
post_data.json
137+
benchmark.local.sh

README.md

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,30 @@ Check the [How to install required packages](./.github/installations.md) guide f
3232

3333
View all end-points details (Open API Specification) : https://web3-api-akbv.chainsaw-dev.zeet.app
3434

35+
### Websocket Listener
36+
37+
For updates on your requests, you can either poll using the `get` (`/tranasction/status/<tx_queue_id>`) method or use websockets. [How to use websockets](./.github/websocket_usage.md)
38+
3539
## Environment Variables
3640

37-
| Variable Name | Description | Default Value | Required |
38-
| ------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------- | -------- |
39-
| `HOST` | Host name of the API Server | `localhost` | false |
40-
| `PORT` | Port number of the API Server | `3005` | false |
41-
| `THIRDWEB_API_KEY` | API Key to access ThirdWeb API | | true |
42-
| `POSTGRES_HOST` | PostgreSQL Host Name | | true |
43-
| `POSTGRES_DATABASE_NAME` | PostgreSQL Database Name | | true |
44-
| `POSTGRES_USER` | PostgreSQL Username | | true |
45-
| `POSTGRES_PASSWORD` | PostgreSQL Password | | true |
46-
| `POSTGRES_PORT` | PostgreSQL Port | | true |
47-
| `POSTGRES_USE_SSL` | Flag to indicate whether to use SSL | | true |
48-
| `TRANSACTIONS_TO_BATCH` | Number of transactions to batch process at a time. | `10` | false |
49-
| `CHAIN_OVERRIDES` | Pass your own RPC urls to override the default ones. This can be file or an URL. See example override-rpc-urls.json | | false |
50-
| `OPENAPI_BASE_ORIGIN` | Base URL for Open API Specification. Should be the Base URL of your App. | `http://localhost:3005` | false |
51-
| `THIRDWEB_SDK_SECRET_KEY` | Create an API KEY on Thirdweb Dashboard and copy the SecretKey. | | true |
41+
| Variable Name | Description | Default Value | Required |
42+
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------- | -------- |
43+
| `HOST` | Host name of the API Server | `localhost` | false |
44+
| `PORT` | Port number of the API Server | `3005` | false |
45+
| `THIRDWEB_API_KEY` | API Key to access ThirdWeb API | | true |
46+
| `POSTGRES_HOST` | PostgreSQL Host Name | | true |
47+
| `POSTGRES_DATABASE_NAME` | PostgreSQL Database Name | | true |
48+
| `POSTGRES_USER` | PostgreSQL Username | | true |
49+
| `POSTGRES_PASSWORD` | PostgreSQL Password | | true |
50+
| `POSTGRES_PORT` | PostgreSQL Port | | true |
51+
| `POSTGRES_USE_SSL` | Flag to indicate whether to use SSL | | true |
52+
| `TRANSACTIONS_TO_BATCH` | Number of transactions to batch process at a time. | `10` | false |
53+
| `CHAIN_OVERRIDES` | Pass your own RPC urls to override the default ones. This can be file or an URL. See example override-rpc-urls.json | | false |
54+
| `OPENAPI_BASE_ORIGIN` | Base URL for Open API Specification. Should be the Base URL of your App. | `http://localhost:3005` | false |
55+
| `THIRDWEB_SDK_SECRET_KEY` | Create an API KEY on Thirdweb Dashboard and copy the SecretKey. | | true |
56+
| `MINED_TX_CRON_ENABLED` | Flag to indicate whether to run the cron job to check mined transactions. | `true` | false |
57+
| `MINED_TX_CRON_SCHEDULE` | Cron Schedule for the cron job to check mined transactions. | `*/30 * * * *` | false |
58+
| `MIN_TX_TO_CHECK_FOR_MINED_STATUS` | Number of transactions to check for mined status at a time. | `50` | false |
5259

5360
## Setup Instructions
5461

core/database/dbConnect.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,14 @@ export const connectToDB = async (
7575
connection,
7676
};
7777

78+
await knex.destroy();
7879
// re-instantiate connection with new config
7980
knex = dbClientPackage(knexConfig);
8081

8182
return knex;
8283
};
8384

84-
export const connectWithDatabase = async (
85-
server: FastifyInstance | FastifyRequest,
86-
): Promise<Knex> => {
85+
export const connectWithDatabase = async (): Promise<Knex> => {
8786
let knexConfig: Knex.Config = {
8887
client: dbClient,
8988
connection,

core/database/dbPrereqs.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ export const checkTablesExistence = async (
1515
server: FastifyInstance,
1616
): Promise<void> => {
1717
try {
18-
// Connect to the DB
1918
const knex = await connectToDB(server);
20-
2119
// Check if the tables Exists
2220
const tablesList: string[] = env.DB_TABLES_LIST.split(",").map(function (
2321
item: any,
@@ -64,7 +62,7 @@ export const implementTriggerOnStartUp = async (
6462
): Promise<void> => {
6563
try {
6664
// Connect to the DB
67-
const knex = await connectWithDatabase(server);
65+
const knex = await connectWithDatabase();
6866

6967
const triggersList: string[] = env.DB_TRIGGERS_LIST.split(",").map(
7068
function (item: any) {

core/database/sql-schemas/trigger_notification.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,16 @@ BEGIN
77
RETURN NEW;
88
END;
99
$function$;
10+
11+
-- Trigger to return only specific column, to keep it under the size limit
12+
CREATE OR REPLACE FUNCTION notify_transactions_update()
13+
RETURNS TRIGGER
14+
LANGUAGE plpgsql
15+
AS $function$
16+
BEGIN
17+
PERFORM pg_notify('updated_transaction_data', json_build_object(
18+
'identifier', NEW.identifier
19+
)::text);
20+
RETURN NEW;
21+
END;
22+
$function$;

core/database/sql-schemas/trigger_tx_table.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ CREATE OR REPLACE TRIGGER transactions_insert_trigger
22
AFTER INSERT ON transactions
33
FOR EACH ROW
44
EXECUTE FUNCTION notify_transactions_insert();
5+
6+
CREATE OR REPLACE TRIGGER transactions_update_trigger
7+
AFTER UPDATE ON transactions
8+
FOR EACH ROW
9+
EXECUTE FUNCTION notify_transactions_update();

core/env.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ export const env = createEnv({
5555
TRANSACTIONS_TO_BATCH: z.coerce.number().default(10),
5656
CHAIN_OVERRIDES: z.string().default(""),
5757
ACCESS_CONTROL_ALLOW_ORIGIN: z.string().default("*"),
58+
MINED_TX_CRON_ENABLED: boolSchema("true"),
59+
MINED_TX_CRON_SCHEDULE: z.string().default("*/30 * * * * *"),
60+
MIN_TX_TO_CHECK_FOR_MINED_STATUS: z.coerce.number().default(50),
5861
},
5962
clientPrefix: "NEVER_USED",
6063
client: {},
@@ -87,6 +90,10 @@ export const env = createEnv({
8790
TRANSACTIONS_TO_BATCH: process.env.TRANSACTIONS_TO_BATCH,
8891
CHAIN_OVERRIDES: process.env.CHAIN_OVERRIDES,
8992
ACCESS_CONTROL_ALLOW_ORIGIN: process.env.ACCESS_CONTROL_ALLOW_ORIGIN,
93+
MINED_TX_CRON_ENABLED: process.env.MINED_TX_CRON_ENABLED,
94+
MINED_TX_CRON_SCHEDULE: process.env.MINED_TX_CRON_SCHEDULE,
95+
MIN_TX_TO_CHECK_FOR_MINED_STATUS:
96+
process.env.MIN_TX_TO_CHECK_FOR_MINED_STATUS,
9097
},
9198
onValidationError: (error: ZodError) => {
9299
if ("WALLET_KEYS" in error.format()) {

e2e/transaction/transaction.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe("Transaction End-point Test", () => {
1717
.set("Authorization", `Bearer ${env.THIRDWEB_SDK_SECRET_KEY}`)
1818
.send();
1919

20-
expect(response.status).to.equal(404);
20+
expect(response.status).to.equal(200);
2121
});
2222

2323
it("should return all transaction requests data", async () => {

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@
3232
"@fastify/swagger": "^8.3.1",
3333
"@fastify/swagger-ui": "^1.8.1",
3434
"@fastify/type-provider-typebox": "^3.2.0",
35+
"@fastify/websocket": "^8.2.0",
3536
"@sinclair/typebox": "^0.28",
3637
"@t3-oss/env-core": "^0.6.0",
37-
"@thirdweb-dev/chains": "^0.1.27",
38-
"@thirdweb-dev/sdk": "^3.10.40",
39-
"@thirdweb-dev/service-utils": "^0.2.1",
40-
"@thirdweb-dev/wallets": "^1.0.4",
38+
"@thirdweb-dev/chains": "^0.1.46",
39+
"@thirdweb-dev/sdk": "^3.10.54",
40+
"@thirdweb-dev/service-utils": "^0.4.2",
41+
"@thirdweb-dev/wallets": "^1.1.18",
4142
"body-parser": "^1.20.2",
4243
"cookie": "^0.5.0",
4344
"copyfiles": "^2.4.1",
@@ -48,6 +49,7 @@
4849
"fastify-plugin": "^4.5.0",
4950
"http-status-codes": "^2.2.0",
5051
"knex": "^2.4.2",
52+
"node-cron": "^3.0.2",
5153
"p-queue": "^7.3.4",
5254
"pg": "^8.11.0",
5355
"pino-pretty": "^10.0.0",
@@ -62,10 +64,11 @@
6264
"@types/express": "^4.17.17",
6365
"@types/mocha": "^10.0.1",
6466
"@types/node": "^18.15.4",
67+
"@types/node-cron": "^3.0.8",
6568
"@types/pg": "^8.6.6",
6669
"@types/supertest": "^2.0.12",
6770
"@types/uuid": "^9.0.1",
68-
"@types/ws": "^8.5.4",
71+
"@types/ws": "^8.5.5",
6972
"@typescript-eslint/eslint-plugin": "^5.55.0",
7073
"@typescript-eslint/parser": "^5.55.0",
7174
"chai": "^4.3.7",

0 commit comments

Comments
 (0)