Skip to content

Commit

Permalink
Listening events on v4 is not working (#6082)
Browse files Browse the repository at this point in the history
* fix for metamask

* add tests

* remove comments

* fix web3 providers

* fix types

* add support for old providers

* Update packages/web3-core/src/web3_subscriptions.ts

Co-authored-by: Wyatt Barnes <me@wyatt.email>

* add changelogs

* fix eip providers. add extra tests

* fix unit tests

---------

Co-authored-by: Wyatt Barnes <me@wyatt.email>
  • Loading branch information
avkos and spacesailor24 authored May 16, 2023
1 parent 072a968 commit e550a9c
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 29 deletions.
1 change: 1 addition & 0 deletions packages/web3-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed

- `getConfig` method from `Web3Config` class, `config` is now public and accessible using `Web3Config.config` (#5950)
- Error param in the `messageListener` in subscription was removed (triggered by `.on('data')` or `.on('message')`) to properly support all providers. (#6082)

## [Unreleased]

Expand Down
32 changes: 24 additions & 8 deletions packages/web3-core/src/web3_subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
JsonRpcSubscriptionResult,
DataFormat,
DEFAULT_RETURN_FORMAT,
JsonRpcSubscriptionResultOld,
EIP1193Provider,
} from 'web3-types';
import { jsonRpc } from 'web3-utils';
import { Web3EventEmitter, Web3EventMap } from './web3_event_emitter';
Expand All @@ -44,7 +46,7 @@ export abstract class Web3Subscription<
private readonly _lastBlock?: BlockOutput;
private readonly _returnFormat: DataFormat;
private _id?: HexString;
private _messageListener?: (e: Error | undefined, data?: JsonRpcNotification<Log>) => void;
private _messageListener?: (data?: JsonRpcNotification<Log>) => void;

public constructor(
public readonly args: ArgsType,
Expand All @@ -71,18 +73,32 @@ export abstract class Web3Subscription<
});

const messageListener = (
err: Error | undefined,
data?: JsonRpcSubscriptionResult | JsonRpcNotification<Log>,
data?:
| JsonRpcSubscriptionResult
| JsonRpcSubscriptionResultOld<Log>
| JsonRpcNotification<Log>,
) => {
if (data && jsonRpc.isResponseWithNotification(data)) {
this._processSubscriptionResult(data?.params.result);
// for EIP-1193 provider
if (data?.data) {
this._processSubscriptionResult(data?.data?.result ?? data?.data);
return;
}
if (err) {
this._processSubscriptionError(err);

if (
data &&
jsonRpc.isResponseWithNotification(
data as unknown as JsonRpcSubscriptionResult | JsonRpcNotification<Log>,
)
) {
this._processSubscriptionResult(data?.params.result);
}
};

(this._requestManager.provider as Web3BaseProvider).on<Log>('message', messageListener);
if (typeof (this._requestManager.provider as EIP1193Provider<API>).request === 'function') {
(this._requestManager.provider as Web3BaseProvider).on<Log>('message', messageListener);
} else {
(this._requestManager.provider as Web3BaseProvider).on<Log>('data', messageListener);
}

this._messageListener = messageListener;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/web3-core/test/unit/web3_subscription.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('Web3Subscription', () => {
requestManager = {
send: jest.fn(),
on: jest.fn(),
provider: { on: jest.fn(), removeListener: jest.fn() },
provider: { on: jest.fn(), removeListener: jest.fn(), request: jest.fn() },
};
sub = new ExampleSubscription({ param1: 'value' }, { requestManager });
});
Expand Down
106 changes: 106 additions & 0 deletions packages/web3-core/test/unit/web3_subscription_old_providers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
This file is part of web3.js.
web3.js 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.
web3.js 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 web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { ExampleSubscription } from './fixtures/example_subscription';
import { Web3EventEmitter } from '../../src/web3_event_emitter';

describe('Web3Subscription', () => {
let requestManager: any;
let eipRequestManager: any;
let provider: Web3EventEmitter<any>;
let eipProvider: Web3EventEmitter<any>;

beforeEach(() => {
provider = new Web3EventEmitter();
eipProvider = new Web3EventEmitter();
// @ts-expect-error add to test eip providers
eipProvider.request = jest.fn();
requestManager = { send: jest.fn(), on: jest.fn(), provider };
eipRequestManager = { send: jest.fn(), on: jest.fn(), provider: eipProvider };
});

describe('providers response for old provider', () => {
it('data with result', async () => {
const testData = {
data: {
result: {
some: 1,
},
},
};
const sub = new ExampleSubscription({ param1: 'param1' }, { requestManager });
await sub.subscribe();
// @ts-expect-error spy on protected method
const processResult = jest.spyOn(sub, '_processSubscriptionResult');
provider.emit('data', testData);
expect(processResult).toHaveBeenCalledWith(testData.data.result);
});

it('data without result for old provider', async () => {
const testData = {
data: {
other: {
some: 1,
},
},
};
const sub = new ExampleSubscription({ param1: 'param1' }, { requestManager });
await sub.subscribe();
// @ts-expect-error spy on protected method
const processResult = jest.spyOn(sub, '_processSubscriptionResult');
provider.emit('data', testData);
expect(processResult).toHaveBeenCalledWith(testData.data);
});
it('data with result for eipProvider', async () => {
const testData = {
data: {
result: {
some: 1,
},
},
};
const sub = new ExampleSubscription(
{ param1: 'param1' },
{ requestManager: eipRequestManager },
);
await sub.subscribe();
// @ts-expect-error spy on protected method
const processResult = jest.spyOn(sub, '_processSubscriptionResult');
eipProvider.emit('message', testData);
expect(processResult).toHaveBeenCalledWith(testData.data.result);
});

it('data without result for eipProvider', async () => {
const testData = {
data: {
other: {
some: 1,
},
},
};
const sub = new ExampleSubscription(
{ param1: 'param1' },
{ requestManager: eipRequestManager },
);
await sub.subscribe();
// @ts-expect-error spy on protected method
const processResult = jest.spyOn(sub, '_processSubscriptionResult');
eipProvider.emit('message', testData);
expect(processResult).toHaveBeenCalledWith(testData.data);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,10 @@ describeIf(isWs)('WebSocketProvider - implemented methods', () => {

describe('subscribe event tests', () => {
it('should subscribe to `message` event', async () => {
const messagePromise = new Promise((resolve: Resolve, reject) => {
const messagePromise = new Promise((resolve: Resolve) => {
webSocketProvider.on(
'message',
(
err: unknown,
result?: JsonRpcSubscriptionResult | JsonRpcNotification<unknown>,
) => {
if (err) reject();
(result?: JsonRpcSubscriptionResult | JsonRpcNotification<unknown>) => {
if (result?.id !== jsonRpcPayload.id) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,6 @@ describe('WebSocketProvider', () => {

expect(result).toEqual(jsonRpcResponse);
});

it('should emit message with response and "null" error', async () => {
const messageSpy = jest.fn();
wsProvider.on('message', messageSpy);

await wsProvider.request(jsonRpcPayload);

expect(messageSpy).toHaveBeenCalledWith(undefined, jsonRpcResponse);
});
});
});
});
4 changes: 3 additions & 1 deletion packages/web3-types/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Added `filters` param to the `Filter` type (#6010)
- Added types `JsonRpcSubscriptionResultOld`, `Web3ProviderMessageEventCallback`. Added `.on('data')` type support for old providers (#6082)

### Changed

- Replaced Buffer for Uint8Array (#6004)
- types `FMT_BYTES.BUFFER`, `Bytes` and `FormatType` and encryption option types for `salt` and `iv` has replaced support for `Buffer` for `Uint8Array` (#6004)
- Added `internalType` property to the `AbiParameter` type.
- Added `internalType` property to the `AbiParameter` type.
10 changes: 10 additions & 0 deletions packages/web3-types/src/json_rpc_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,21 @@ export interface SubscriptionParams<T = JsonRpcResult> {
readonly subscription: string; // for subscription id
readonly result: T;
}

export interface JsonRpcSubscriptionResultOld<T = JsonRpcResult> {
readonly error?: never;
readonly params?: never;
readonly type: string;
readonly data: SubscriptionParams<T>;
}

export interface JsonRpcNotification<T = JsonRpcResult> {
readonly id?: JsonRpcId;
readonly jsonrpc: JsonRpcIdentifier;
readonly method: string; // for subscription
readonly params: SubscriptionParams<T>; // for subscription results
readonly result: never;
readonly data?: never;
}

export interface JsonRpcSubscriptionResult {
Expand All @@ -56,6 +65,7 @@ export interface JsonRpcSubscriptionResult {
readonly result: string;
readonly method: never;
readonly params: never;
readonly data?: never;
}

export interface JsonRpcRequest<T = unknown[]> {
Expand Down
15 changes: 14 additions & 1 deletion packages/web3-types/src/web3_base_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export type Web3ProviderEventCallback<T = JsonRpcResult> = (
result?: JsonRpcSubscriptionResult | JsonRpcNotification<T>,
) => void;

export type Web3ProviderMessageEventCallback<T = JsonRpcResult> = (
result?: JsonRpcSubscriptionResult | JsonRpcNotification<T>,
) => void;

export type Web3Eip1193ProviderEventCallback<T> = (data: T) => void;

export type Web3ProviderRequestCallback<ResultType = unknown> = (
Expand Down Expand Up @@ -166,7 +170,16 @@ export abstract class Web3BaseProvider<API extends Web3APISpec = EthExecutionAPI
): void;
public abstract on<T = JsonRpcResult>(
type: 'message' | string,
listener: Web3Eip1193ProviderEventCallback<ProviderMessage> | Web3ProviderEventCallback<T>,
listener:
| Web3Eip1193ProviderEventCallback<ProviderMessage>
| Web3ProviderMessageEventCallback<T>,
): void;
// for old providers
public abstract on<T = JsonRpcResult>(
type: 'data' | string,
listener:
| Web3Eip1193ProviderEventCallback<ProviderMessage>
| Web3ProviderMessageEventCallback<T>,
): void;
public abstract on(
type: 'connect',
Expand Down
9 changes: 6 additions & 3 deletions packages/web3-utils/src/socket_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
Web3APISpec,
Web3Eip1193ProviderEventCallback,
Web3ProviderEventCallback,
Web3ProviderMessageEventCallback,
Web3ProviderStatus,
} from 'web3-types';
import {
Expand Down Expand Up @@ -213,7 +214,9 @@ export abstract class SocketProvider<
public on(type: 'accountsChanged', listener: Web3Eip1193ProviderEventCallback<string[]>): void;
public on<T = JsonRpcResult>(
type: 'message',
listener: Web3Eip1193ProviderEventCallback<ProviderMessage> | Web3ProviderEventCallback<T>,
listener:
| Web3Eip1193ProviderEventCallback<ProviderMessage>
| Web3ProviderMessageEventCallback<T>,
): void;
public on<T = JsonRpcResult>(
type: string,
Expand Down Expand Up @@ -460,7 +463,7 @@ export abstract class SocketProvider<
jsonRpc.isResponseWithNotification(response as JsonRpcNotification) &&
(response as JsonRpcNotification).method.endsWith('_subscription')
) {
this._eventEmitter.emit('message', undefined, response);
this._eventEmitter.emit('message', response);
return;
}

Expand All @@ -479,7 +482,7 @@ export abstract class SocketProvider<
jsonRpc.isResponseWithResult(response) ||
jsonRpc.isResponseWithError(response)
) {
this._eventEmitter.emit('message', undefined, response);
this._eventEmitter.emit('message', response);
requestItem.deferredPromise.resolve(response);
}

Expand Down

0 comments on commit e550a9c

Please sign in to comment.