From 4cbb35e65312ea906e0ef230dfc616ee5912d6c3 Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Mon, 21 Oct 2024 10:50:43 +0200 Subject: [PATCH 1/9] websockets on the http rest port --- docker/devnet.conf | 1 - docker/docker-compose.yml | 1 - packages/web3/src/api/api-alephium.ts | 30 ++++++++++----------- packages/web3/src/fixtures/self-clique.json | 3 +-- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/docker/devnet.conf b/docker/devnet.conf index 0be6da031..5ac8e23a8 100644 --- a/docker/devnet.conf +++ b/docker/devnet.conf @@ -38,7 +38,6 @@ alephium.node.event-log.index-by-tx-id = true alephium.node.event-log.index-by-block-hash = true alephium.network.rest-port = 22973 -alephium.network.ws-port = 21973 alephium.network.miner-api-port = 20973 alephium.api.network-interface = "0.0.0.0" alephium.api.api-key-enabled = false diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index cb256f2f5..6d020f54d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -35,7 +35,6 @@ services: - 19973:19973/tcp - 19973:19973/udp - 127.0.0.1:20973:20973 - - 127.0.0.1:21973:21973 - 127.0.0.1:22973:22973 environment: - ALEPHIUM_LOG_LEVEL=DEBUG diff --git a/packages/web3/src/api/api-alephium.ts b/packages/web3/src/api/api-alephium.ts index e4332a952..0042a9346 100644 --- a/packages/web3/src/api/api-alephium.ts +++ b/packages/web3/src/api/api-alephium.ts @@ -909,8 +909,6 @@ export interface PeerAddress { /** @format int32 */ restPort: number /** @format int32 */ - wsPort: number - /** @format int32 */ minerApiPort: number } @@ -1545,8 +1543,8 @@ export class HttpClient { property instanceof Blob ? property : typeof property === 'object' && property !== null - ? JSON.stringify(property) - : `${property}` + ? JSON.stringify(property) + : `${property}` ) return formData }, new FormData()), @@ -1626,18 +1624,18 @@ export class HttpClient { const data = !responseFormat ? r : await response[responseFormat]() - .then((data) => { - if (r.ok) { - r.data = data - } else { - r.error = data - } - return r - }) - .catch((e) => { - r.error = e - return r - }) + .then((data) => { + if (r.ok) { + r.data = data + } else { + r.error = data + } + return r + }) + .catch((e) => { + r.error = e + return r + }) if (cancelToken) { this.abortControllers.delete(cancelToken) diff --git a/packages/web3/src/fixtures/self-clique.json b/packages/web3/src/fixtures/self-clique.json index 7222df936..38da0106d 100644 --- a/packages/web3/src/fixtures/self-clique.json +++ b/packages/web3/src/fixtures/self-clique.json @@ -7,7 +7,6 @@ { "address": "127.0.0.1", "restPort": 12973, - "wsPort": 11973, "minerApiPort": 10973 } ], @@ -16,4 +15,4 @@ "groupNumPerBroker": 4, "groups": 4 } -} +} \ No newline at end of file From d1b2cee81ef5b158d23c00908adcf99ba66ee843 Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Sat, 9 Nov 2024 20:40:45 +0100 Subject: [PATCH 2/9] websocket-test-for-block-events --- package.json | 4 ++- test/websocket.test.ts | 76 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 test/websocket.test.ts diff --git a/package.json b/package.json index 52740dde8..2693d80b2 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,14 @@ "@alephium/cli": "workspace:*", "@alephium/web3": "workspace:*", "@alephium/web3-test": "workspace:*", - "@alephium/web3-wallet": "workspace:*" + "@alephium/web3-wallet": "workspace:*", + "ws": "^8.5.4" }, "devDependencies": { "@swc/core": "^1.4.1", "@types/jest": "^28.1.8", "@types/node": "^16.18.23", + "@types/ws": "^8.5.4", "bip39": "3.0.4", "eslint": "^8.37.0", "eslint-plugin-jest": "^27.2.1", diff --git a/test/websocket.test.ts b/test/websocket.test.ts new file mode 100644 index 000000000..2e06f2d9b --- /dev/null +++ b/test/websocket.test.ts @@ -0,0 +1,76 @@ +/* +Copyright 2018 - 2022 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ + +import WebSocket from 'ws'; +import { DEFAULT_GAS_ALPH_AMOUNT, ONE_ALPH, NodeProvider, sleep, web3 } from '@alephium/web3'; +import { getSigner } from '@alephium/web3-test'; + +const WS_ENDPOINT = 'ws://127.0.0.1:22973/ws'; + +describe('WebSocket and Transaction Test', () => { + let signer; + + beforeAll(async () => { + web3.setCurrentNodeProvider('http://127.0.0.1:22973', undefined, fetch); + signer = await getSigner(); + }); + + it('should connect, subscribe, and receive block event after a transaction is submitted', async () => { + const ws = new WebSocket(WS_ENDPOINT); + + await new Promise((resolve, reject) => { + ws.on('open', async () => { + try { + ws.send('subscribe:block'); + + const address = (await signer.getSelectedAccount()).address; + const fee = 0.01; + const attoAlphAmount = ONE_ALPH; + + await signer.signAndSubmitTransferTx({ + signerAddress: address, + destinations: [{ address, attoAlphAmount }], + fee, + }); + } catch (error) { + console.error('Error during transaction submission:', error); + reject(error); + } + }); + + ws.on('message', (data) => { + try { + console.log('Received:', data.toString()); + const parsedData = JSON.parse(data.toString()); + expect(parsedData).toBeDefined(); + expect(parsedData.method).toBe('block'); + resolve(); + } catch (error) { + reject(error); + } finally { + ws.close(); + } + }); + + ws.on('error', (error) => { + console.error('WebSocket error:', error); + reject(error); + }); + }); + }); +}); From 6f310f56501ef3c528be9cab4da56823e635b89e Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Mon, 11 Nov 2024 12:40:20 +0100 Subject: [PATCH 3/9] WebSocketClient --- packages/web3/src/index.ts | 1 + packages/web3/src/ws/index.ts | 19 ++++++ packages/web3/src/ws/websocket-client.ts | 76 ++++++++++++++++++++++++ test/websocket.test.ts | 71 +++++++++------------- 4 files changed, 124 insertions(+), 43 deletions(-) create mode 100644 packages/web3/src/ws/index.ts create mode 100644 packages/web3/src/ws/websocket-client.ts diff --git a/packages/web3/src/index.ts b/packages/web3/src/index.ts index f1883aac3..9fdb5e659 100644 --- a/packages/web3/src/index.ts +++ b/packages/web3/src/index.ts @@ -26,6 +26,7 @@ export * from './signer' export * from './utils' export * from './transaction' export * from './token' +export * from './ws' export * from './constants' export * as web3 from './global' diff --git a/packages/web3/src/ws/index.ts b/packages/web3/src/ws/index.ts new file mode 100644 index 000000000..14d138e27 --- /dev/null +++ b/packages/web3/src/ws/index.ts @@ -0,0 +1,19 @@ +/* +Copyright 2018 - 2022 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ + +export { WebSocketClient } from './websocket-client' diff --git a/packages/web3/src/ws/websocket-client.ts b/packages/web3/src/ws/websocket-client.ts new file mode 100644 index 000000000..0832eeddc --- /dev/null +++ b/packages/web3/src/ws/websocket-client.ts @@ -0,0 +1,76 @@ +/* +Copyright 2018 - 2022 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ + +import WebSocket from 'ws'; + +type WsMessageType = string; +type WsSubscription = [WsMessageType, (params: any) => void]; + +export class WebSocketClient { + private ws: WebSocket; + private subscriptions: WsSubscription[] = []; + + constructor(endpoint: string, eventSubscriptions: WsSubscription[] = []) { + this.ws = new WebSocket(endpoint); + eventSubscriptions.forEach(([messageType, callback]) => { + this.subscriptions.push([messageType, callback]); + }); + + this.ws.on('open', () => { + console.log('WebSocket connection opened'); + this.subscribeToEvents(); + }); + + this.ws.on('message', (data) => { + try { + const parsedData = JSON.parse(data.toString()); + this.subscriptions.forEach(([method, callback]) => { + if (parsedData.method === method) { + callback(parsedData.params); + } + }); + } catch (error) { + console.error('Error parsing message:', error); + } + }); + + this.ws.on('error', (error) => { + console.error('WebSocket error:', error); + }); + + this.ws.on('close', () => { + console.log('WebSocket connection closed'); + }); + } + + private subscribeToEvents() { + this.subscriptions.forEach(([method]) => { + console.log(`Subscribing to ${method}`); + this.ws.send(`subscribe:${method}`); + }); + } + + onOpen(callback: () => void) { + this.ws.on('open', callback); + } + + close() { + this.ws.close(); + } +} + diff --git a/test/websocket.test.ts b/test/websocket.test.ts index 2e06f2d9b..d88dfd555 100644 --- a/test/websocket.test.ts +++ b/test/websocket.test.ts @@ -16,14 +16,14 @@ You should have received a copy of the GNU Lesser General Public License along with the library. If not, see . */ -import WebSocket from 'ws'; -import { DEFAULT_GAS_ALPH_AMOUNT, ONE_ALPH, NodeProvider, sleep, web3 } from '@alephium/web3'; +import { ONE_ALPH, SignerProviderSimple, web3, WebSocketClient } from '@alephium/web3'; import { getSigner } from '@alephium/web3-test'; const WS_ENDPOINT = 'ws://127.0.0.1:22973/ws'; -describe('WebSocket and Transaction Test', () => { - let signer; +describe('WebSocket', () => { + let signer: SignerProviderSimple; + let wsClient: WebSocketClient; beforeAll(async () => { web3.setCurrentNodeProvider('http://127.0.0.1:22973', undefined, fetch); @@ -31,45 +31,30 @@ describe('WebSocket and Transaction Test', () => { }); it('should connect, subscribe, and receive block event after a transaction is submitted', async () => { - const ws = new WebSocket(WS_ENDPOINT); - - await new Promise((resolve, reject) => { - ws.on('open', async () => { - try { - ws.send('subscribe:block'); - - const address = (await signer.getSelectedAccount()).address; - const fee = 0.01; - const attoAlphAmount = ONE_ALPH; - - await signer.signAndSubmitTransferTx({ - signerAddress: address, - destinations: [{ address, attoAlphAmount }], - fee, - }); - } catch (error) { - console.error('Error during transaction submission:', error); - reject(error); - } - }); - - ws.on('message', (data) => { - try { - console.log('Received:', data.toString()); - const parsedData = JSON.parse(data.toString()); - expect(parsedData).toBeDefined(); - expect(parsedData.method).toBe('block'); - resolve(); - } catch (error) { - reject(error); - } finally { - ws.close(); - } - }); - - ws.on('error', (error) => { - console.error('WebSocket error:', error); - reject(error); + await new Promise((resolve, reject) => { + wsClient = new WebSocketClient(WS_ENDPOINT, [ + ['block', async (params: any) => { + console.log('Received block event:', params); + expect(params).toBeDefined(); + }], + ]); + wsClient.onOpen(() => { + (async () => { + try { + const address = (await signer.getSelectedAccount()).address; + const attoAlphAmount = ONE_ALPH; + + await signer.signAndSubmitTransferTx({ + signerAddress: address, + destinations: [{ address, attoAlphAmount }], + }); + resolve(true); + wsClient.close(); + } catch (error) { + console.error('Error during transaction submission:', error); + reject(error); + } + })(); }); }); }); From 28a182175aab9659ffe9386fce6608892492fef4 Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Mon, 11 Nov 2024 12:43:35 +0100 Subject: [PATCH 4/9] rename websocket-client tests --- test/{websocket.test.ts => websocket-client.test.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename test/{websocket.test.ts => websocket-client.test.ts} (98%) diff --git a/test/websocket.test.ts b/test/websocket-client.test.ts similarity index 98% rename from test/websocket.test.ts rename to test/websocket-client.test.ts index d88dfd555..386a3458d 100644 --- a/test/websocket.test.ts +++ b/test/websocket-client.test.ts @@ -21,7 +21,7 @@ import { getSigner } from '@alephium/web3-test'; const WS_ENDPOINT = 'ws://127.0.0.1:22973/ws'; -describe('WebSocket', () => { +describe('WebSocketClient', () => { let signer: SignerProviderSimple; let wsClient: WebSocketClient; From ff964e09b6ecda03d672692c4f4a82c6b3ecc24d Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Fri, 22 Nov 2024 13:07:20 +0100 Subject: [PATCH 5/9] using event emitter --- packages/web3/src/ws/websocket-client.ts | 35 +++++++++++------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/web3/src/ws/websocket-client.ts b/packages/web3/src/ws/websocket-client.ts index 0832eeddc..e685bcdf3 100644 --- a/packages/web3/src/ws/websocket-client.ts +++ b/packages/web3/src/ws/websocket-client.ts @@ -17,33 +17,28 @@ along with the library. If not, see . */ import WebSocket from 'ws'; +import EventEmitter from 'eventemitter3'; -type WsMessageType = string; -type WsSubscription = [WsMessageType, (params: any) => void]; +export type WsMessageType = string; +export type WsSubscription = [WsMessageType, (params: any) => void]; -export class WebSocketClient { +export class WebSocketClient extends EventEmitter { private ws: WebSocket; - private subscriptions: WsSubscription[] = []; constructor(endpoint: string, eventSubscriptions: WsSubscription[] = []) { + super(); this.ws = new WebSocket(endpoint); - eventSubscriptions.forEach(([messageType, callback]) => { - this.subscriptions.push([messageType, callback]); - }); this.ws.on('open', () => { console.log('WebSocket connection opened'); - this.subscribeToEvents(); + this.emit('open'); + this.subscribeToEvents(eventSubscriptions); }); this.ws.on('message', (data) => { try { const parsedData = JSON.parse(data.toString()); - this.subscriptions.forEach(([method, callback]) => { - if (parsedData.method === method) { - callback(parsedData.params); - } - }); + this.emit(parsedData.method, parsedData.params); } catch (error) { console.error('Error parsing message:', error); } @@ -51,17 +46,20 @@ export class WebSocketClient { this.ws.on('error', (error) => { console.error('WebSocket error:', error); + this.emit('error', error); }); this.ws.on('close', () => { console.log('WebSocket connection closed'); + this.emit('close'); }); } - private subscribeToEvents() { - this.subscriptions.forEach(([method]) => { - console.log(`Subscribing to ${method}`); - this.ws.send(`subscribe:${method}`); + private subscribeToEvents(eventSubscriptions: WsSubscription[]) { + eventSubscriptions.forEach(([messageType, callback]) => { + this.on(messageType, callback); + console.log(`Subscribing to ${messageType}`); + this.ws.send(`subscribe:${messageType}`); }); } @@ -72,5 +70,4 @@ export class WebSocketClient { close() { this.ws.close(); } -} - +} \ No newline at end of file From 49f56d96f4b3c3dfb6ccd4f936da5be243f6f427 Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Mon, 25 Nov 2024 12:50:21 +0100 Subject: [PATCH 6/9] fixing deps --- package.json | 4 +--- packages/web3/package.json | 2 ++ packages/web3/src/ws/websocket-client.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2693d80b2..52740dde8 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,12 @@ "@alephium/cli": "workspace:*", "@alephium/web3": "workspace:*", "@alephium/web3-test": "workspace:*", - "@alephium/web3-wallet": "workspace:*", - "ws": "^8.5.4" + "@alephium/web3-wallet": "workspace:*" }, "devDependencies": { "@swc/core": "^1.4.1", "@types/jest": "^28.1.8", "@types/node": "^16.18.23", - "@types/ws": "^8.5.4", "bip39": "3.0.4", "eslint": "^8.37.0", "eslint-plugin-jest": "^27.2.1", diff --git a/packages/web3/package.json b/packages/web3/package.json index 3bf3caa18..38e73fe43 100644 --- a/packages/web3/package.json +++ b/packages/web3/package.json @@ -46,6 +46,7 @@ }, "type": "commonjs", "dependencies": { + "ws": "^8.5.4", "@noble/secp256k1": "1.7.1", "base-x": "4.0.0", "bignumber.js": "^9.1.1", @@ -67,6 +68,7 @@ "@types/mock-fs": "^4.13.1", "@types/node": "^16.18.23", "@types/rewire": "^2.5.28", + "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "clean-webpack-plugin": "4.0.0", diff --git a/packages/web3/src/ws/websocket-client.ts b/packages/web3/src/ws/websocket-client.ts index e685bcdf3..1729ab39e 100644 --- a/packages/web3/src/ws/websocket-client.ts +++ b/packages/web3/src/ws/websocket-client.ts @@ -70,4 +70,4 @@ export class WebSocketClient extends EventEmitter { close() { this.ws.close(); } -} \ No newline at end of file +} From d5e9d4c8fdb493979f62415c5c34f96c454aef34 Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Thu, 2 Jan 2025 17:42:41 +0100 Subject: [PATCH 7/9] improving ws client and tests --- packages/web3/src/ws/websocket-client.ts | 121 +++++++++++++++++------ test/websocket-client.test.ts | 97 ++++++++++++------ 2 files changed, 158 insertions(+), 60 deletions(-) diff --git a/packages/web3/src/ws/websocket-client.ts b/packages/web3/src/ws/websocket-client.ts index 1729ab39e..85ee3023f 100644 --- a/packages/web3/src/ws/websocket-client.ts +++ b/packages/web3/src/ws/websocket-client.ts @@ -15,59 +15,124 @@ GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the library. If not, see . */ - import WebSocket from 'ws'; -import EventEmitter from 'eventemitter3'; - -export type WsMessageType = string; -export type WsSubscription = [WsMessageType, (params: any) => void]; +import { EventEmitter } from 'eventemitter3'; export class WebSocketClient extends EventEmitter { private ws: WebSocket; + private requestId: number; + private isConnected: boolean; + private notifications: any[]; - constructor(endpoint: string, eventSubscriptions: WsSubscription[] = []) { + constructor(url: string) { super(); - this.ws = new WebSocket(endpoint); + this.ws = new WebSocket(url); + this.requestId = 0; + this.isConnected = false; + this.notifications = []; this.ws.on('open', () => { - console.log('WebSocket connection opened'); - this.emit('open'); - this.subscribeToEvents(eventSubscriptions); + this.isConnected = true; + this.emit('connected'); }); - this.ws.on('message', (data) => { + this.ws.on('message', (data: WebSocket.Data) => { try { - const parsedData = JSON.parse(data.toString()); - this.emit(parsedData.method, parsedData.params); + const message = JSON.parse(data.toString()); + if (message.method === 'subscription') { + // Emit and store notifications + const params = message.params; + this.notifications.push(params); + this.emit('notification', params); + } else { + this.emit(`response_${message.id}`, message); + } } catch (error) { - console.error('Error parsing message:', error); + this.emit('error', error); } }); - this.ws.on('error', (error) => { - console.error('WebSocket error:', error); - this.emit('error', error); + this.ws.on('close', () => { + this.isConnected = false; + this.emit('disconnected'); }); + } - this.ws.on('close', () => { - console.log('WebSocket connection closed'); - this.emit('close'); + public subscribe(method: string, params: unknown[] = []): Promise { + const id = this.getRequestId(); + const request = { + jsonrpc: '2.0', + id, + method, + params + }; + + return new Promise((resolve, reject) => { + this.once(`response_${id}`, (response) => { + if (response.result) { + resolve(response.result); + } else { + reject(response.error); + } + }); + this.ws.send(JSON.stringify(request)); }); } - private subscribeToEvents(eventSubscriptions: WsSubscription[]) { - eventSubscriptions.forEach(([messageType, callback]) => { - this.on(messageType, callback); - console.log(`Subscribing to ${messageType}`); - this.ws.send(`subscribe:${messageType}`); + public unsubscribe(subscriptionId: string): Promise { + const id = this.getRequestId(); + const request = { + jsonrpc: '2.0', + id, + method: 'unsubscribe', + params: [subscriptionId] + }; + + return new Promise((resolve, reject) => { + this.once(`response_${id}`, (response) => { + if (response.result === true) { + resolve(true); + } else { + reject(response.error); + } + }); + this.ws.send(JSON.stringify(request)); }); } - onOpen(callback: () => void) { - this.ws.on('open', callback); + public async subscribeToBlock(): Promise { + return this.subscribe('subscribe', ['block']); + } + + public async subscribeToTx(): Promise { + return this.subscribe('subscribe', ['tx']); } - close() { + public async subscribeToContractEvents(eventIndex: number, addresses: string[]): Promise { + return this.subscribe('subscribe', ['contract', eventIndex, addresses]); + } + + public onConnected(callback: () => void) { + if (this.isConnected) { + callback(); + } else { + this.on('connected', callback); + } + } + + public onNotification(callback: (params: any) => void) { + for (const notification of this.notifications) { + callback(notification); + } + + this.on('notification', callback); + } + + public disconnect() { this.ws.close(); } + + private getRequestId(): number { + return ++this.requestId; + } } diff --git a/test/websocket-client.test.ts b/test/websocket-client.test.ts index 386a3458d..016812bdd 100644 --- a/test/websocket-client.test.ts +++ b/test/websocket-client.test.ts @@ -16,46 +16,79 @@ You should have received a copy of the GNU Lesser General Public License along with the library. If not, see . */ -import { ONE_ALPH, SignerProviderSimple, web3, WebSocketClient } from '@alephium/web3'; -import { getSigner } from '@alephium/web3-test'; +import {ONE_ALPH, SignerProviderSimple, SignTransferTxResult, utils, web3, WebSocketClient} from '@alephium/web3'; +import {getSigner, randomContractAddress} from '@alephium/web3-test'; +const NODE_PROVIDER = 'http://127.0.0.1:22973' const WS_ENDPOINT = 'ws://127.0.0.1:22973/ws'; describe('WebSocketClient', () => { + let client: WebSocketClient; let signer: SignerProviderSimple; - let wsClient: WebSocketClient; - - beforeAll(async () => { - web3.setCurrentNodeProvider('http://127.0.0.1:22973', undefined, fetch); + + async function signAndSubmitTx(): Promise { + const address = (await signer.getSelectedAccount()).address; + const attoAlphAmount = ONE_ALPH; + return await signer.signAndSubmitTransferTx({ + signerAddress: address, + destinations: [{ address, attoAlphAmount }], + }); + } + + beforeEach(async () => { + client = new WebSocketClient(WS_ENDPOINT); signer = await getSigner(); + web3.setCurrentNodeProvider(NODE_PROVIDER, undefined, fetch); + }); + + afterEach(() => { + client.disconnect(); }); - it('should connect, subscribe, and receive block event after a transaction is submitted', async () => { - await new Promise((resolve, reject) => { - wsClient = new WebSocketClient(WS_ENDPOINT, [ - ['block', async (params: any) => { - console.log('Received block event:', params); - expect(params).toBeDefined(); - }], - ]); - wsClient.onOpen(() => { - (async () => { - try { - const address = (await signer.getSelectedAccount()).address; - const attoAlphAmount = ONE_ALPH; - - await signer.signAndSubmitTransferTx({ - signerAddress: address, - destinations: [{ address, attoAlphAmount }], - }); - resolve(true); - wsClient.close(); - } catch (error) { - console.error('Error during transaction submission:', error); - reject(error); - } - })(); - }); + test('should subscribe, receive notifications and unsubscribe', (done) => { + let notificationCount = 0; + let blockNotificationReceived = false; + let txNotificationReceived = false; + let blockSubscriptionId: string; + let txSubscriptionId: string; + let contractEventsSubscriptionId: string; + + + client.onConnected(async () => { + try { + blockSubscriptionId = await client.subscribeToBlock(); + txSubscriptionId = await client.subscribeToTx(); + contractEventsSubscriptionId = await client.subscribeToContractEvents(0, [randomContractAddress()]); + await signAndSubmitTx(); + } catch (error) { + done(error); + } + }); + + client.onNotification(async (params) => { + expect(params).toBeDefined(); + if (params.result.block) { + blockNotificationReceived = true; + } else if (params.result.unsigned) { + txNotificationReceived = true; + } + + notificationCount += 1; + if (notificationCount === 2) { + try { + expect(blockNotificationReceived).toBe(true); + expect(txNotificationReceived).toBe(true); + const blockUnsubscriptionResponse = await client.unsubscribe(blockSubscriptionId); + expect(blockUnsubscriptionResponse).toBe(true); + const txUnsubscriptionResponse = await client.unsubscribe(txSubscriptionId); + expect(txUnsubscriptionResponse).toBe(true); + const contractEventsUnsubscriptionResponse = await client.unsubscribe(contractEventsSubscriptionId); + expect(contractEventsUnsubscriptionResponse).toBe(true); + done(); + } catch (error) { + done(error); + } + } }); }); }); From ad541e3481cf1e183cbe80c750b70a6b849d5f9d Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Thu, 2 Jan 2025 17:51:14 +0100 Subject: [PATCH 8/9] reverting unwanted changes --- packages/web3/src/api/api-alephium.ts | 28 ++++++++++----------- packages/web3/src/fixtures/self-clique.json | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/web3/src/api/api-alephium.ts b/packages/web3/src/api/api-alephium.ts index 0762ae160..793a106aa 100644 --- a/packages/web3/src/api/api-alephium.ts +++ b/packages/web3/src/api/api-alephium.ts @@ -1540,8 +1540,8 @@ export class HttpClient { property instanceof Blob ? property : typeof property === 'object' && property !== null - ? JSON.stringify(property) - : `${property}` + ? JSON.stringify(property) + : `${property}` ) return formData }, new FormData()), @@ -1621,18 +1621,18 @@ export class HttpClient { const data = !responseFormat ? r : await response[responseFormat]() - .then((data) => { - if (r.ok) { - r.data = data - } else { - r.error = data - } - return r - }) - .catch((e) => { - r.error = e - return r - }) + .then((data) => { + if (r.ok) { + r.data = data + } else { + r.error = data + } + return r + }) + .catch((e) => { + r.error = e + return r + }) if (cancelToken) { this.abortControllers.delete(cancelToken) diff --git a/packages/web3/src/fixtures/self-clique.json b/packages/web3/src/fixtures/self-clique.json index 38da0106d..ab0d1c2f7 100644 --- a/packages/web3/src/fixtures/self-clique.json +++ b/packages/web3/src/fixtures/self-clique.json @@ -15,4 +15,4 @@ "groupNumPerBroker": 4, "groups": 4 } -} \ No newline at end of file +} From 6196a5dbb45401d7bb3e2099acaa28b491607775 Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Tue, 14 Jan 2025 15:41:04 +0100 Subject: [PATCH 9/9] changing ws contract event format --- packages/web3/src/ws/websocket-client.ts | 8 ++++++-- test/websocket-client.test.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/web3/src/ws/websocket-client.ts b/packages/web3/src/ws/websocket-client.ts index 85ee3023f..7ffe59f52 100644 --- a/packages/web3/src/ws/websocket-client.ts +++ b/packages/web3/src/ws/websocket-client.ts @@ -108,8 +108,12 @@ export class WebSocketClient extends EventEmitter { return this.subscribe('subscribe', ['tx']); } - public async subscribeToContractEvents(eventIndex: number, addresses: string[]): Promise { - return this.subscribe('subscribe', ['contract', eventIndex, addresses]); + public async subscribeToContractEvents(addresses: string[]): Promise { + return this.subscribe('subscribe', ['contract', {addresses: addresses}]); + } + + public async subscribeToFilteredContractEvents(eventIndex: number, addresses: string[]): Promise { + return this.subscribe('subscribe', ['contract', {eventIndex: eventIndex, addresses: addresses}]); } public onConnected(callback: () => void) { diff --git a/test/websocket-client.test.ts b/test/websocket-client.test.ts index 016812bdd..e89bf1918 100644 --- a/test/websocket-client.test.ts +++ b/test/websocket-client.test.ts @@ -58,7 +58,7 @@ describe('WebSocketClient', () => { try { blockSubscriptionId = await client.subscribeToBlock(); txSubscriptionId = await client.subscribeToTx(); - contractEventsSubscriptionId = await client.subscribeToContractEvents(0, [randomContractAddress()]); + contractEventsSubscriptionId = await client.subscribeToContractEvents([randomContractAddress()]); await signAndSubmitTx(); } catch (error) { done(error);