Skip to content

Commit

Permalink
Merge branch 'main' into eip-6963
Browse files Browse the repository at this point in the history
  • Loading branch information
jiexi committed Sep 27, 2023
2 parents 11a15a9 + 101eb3a commit 2de3189
Show file tree
Hide file tree
Showing 18 changed files with 425 additions and 251 deletions.
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [13.0.0]
### Changed
- **BREAKING**: Update `chainId`, `networkVersion`, and `selectedAddress` to be read-only ([#280](https://github.com/MetaMask/providers/pull/280))
- Log deprecation warning when accessing `chainId`, `networkVersion`, and `selectedAddress` ([#280](https://github.com/MetaMask/providers/pull/280))
- Remove `pump` ([#281](https://github.com/MetaMask/providers/pull/281))

## [12.0.0]
### Changed
- **BREAKING**: Replace `eth-rpc-errors`@`^4.0.2` with `@metamask/rpc-errors`@`6.0.0` ([#277](https://github.com/MetaMask/providers/pull/277))
- **BREAKING**: Replace `json-rpc-engine`@`^6.1.0` with `@metamask/json-rpc-engine`@`7.1.1` ([#277](https://github.com/MetaMask/providers/pull/277))
- Upgrade `@metamask/utils` from `^6.2.0` to `^8.1.0` ([#277](https://github.com/MetaMask/providers/pull/277))

## [11.1.2]
### Changed
- Update `extension-port-stream` to `^2.1.1` ([#273](https://github.com/MetaMask/providers/pull/273))

## [11.1.1]
### Changed
- Update `fast-deep-equal` ([#258](https://github.com/MetaMask/providers/pull/258))
Expand Down Expand Up @@ -228,7 +244,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
added deprecation warnings for them ([#30](https://github.com/MetaMask/providers/pull/30))
- Un-deprecated `sendAsync` ([#29](https://github.com/MetaMask/providers/pull/29))

[Unreleased]: https://github.com/MetaMask/providers/compare/v11.1.1...HEAD
[Unreleased]: https://github.com/MetaMask/providers/compare/v13.0.0...HEAD
[13.0.0]: https://github.com/MetaMask/providers/compare/v12.0.0...v13.0.0
[12.0.0]: https://github.com/MetaMask/providers/compare/v11.1.2...v12.0.0
[11.1.2]: https://github.com/MetaMask/providers/compare/v11.1.1...v11.1.2
[11.1.1]: https://github.com/MetaMask/providers/compare/v11.1.0...v11.1.1
[11.1.0]: https://github.com/MetaMask/providers/compare/v11.0.0...v11.1.0
[11.0.0]: https://github.com/MetaMask/providers/compare/v10.2.1...v11.0.0
Expand Down
8 changes: 4 additions & 4 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ const baseConfig = {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 64.25,
functions: 64.89,
lines: 66.35,
statements: 66.43,
branches: 64.78,
functions: 66,
lines: 66.81,
statements: 66.88,
},
},

Expand Down
12 changes: 5 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metamask/providers",
"version": "11.1.1",
"version": "13.0.0",
"description": "A JavaScript Ethereum provider that connects to the wallet over a stream.",
"keywords": [
"MetaMask",
Expand Down Expand Up @@ -37,17 +37,16 @@
"test:watch": "jest --watch"
},
"dependencies": {
"@metamask/json-rpc-engine": "^7.1.1",
"@metamask/object-multiplex": "^1.1.0",
"@metamask/rpc-errors": "^6.0.0",
"@metamask/safe-event-emitter": "^3.0.0",
"@metamask/utils": "^5.0.2",
"@metamask/utils": "^8.1.0",
"detect-browser": "^5.2.0",
"eth-rpc-errors": "^4.0.2",
"extension-port-stream": "^2.0.1",
"extension-port-stream": "^2.1.1",
"fast-deep-equal": "^3.1.3",
"is-stream": "^2.0.0",
"json-rpc-engine": "^6.1.0",
"json-rpc-middleware-stream": "^4.2.1",
"pump": "^3.0.0",
"webextension-polyfill": "^0.10.0"
},
"devDependencies": {
Expand All @@ -60,7 +59,6 @@
"@types/chrome": "^0.0.233",
"@types/jest": "^28.1.6",
"@types/node": "^17.0.23",
"@types/pump": "^1.1.0",
"@types/readable-stream": "^2.3.15",
"@types/uuid": "^9.0.1",
"@types/webextension-polyfill": "^0.10.0",
Expand Down
73 changes: 40 additions & 33 deletions src/BaseProvider.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { JsonRpcEngine, JsonRpcMiddleware } from '@metamask/json-rpc-engine';
import { rpcErrors, JsonRpcError } from '@metamask/rpc-errors';
import SafeEventEmitter from '@metamask/safe-event-emitter';
import { ethErrors, EthereumRpcError } from 'eth-rpc-errors';
import dequal from 'fast-deep-equal';
import {
JsonRpcEngine,
JsonRpcRequest,
JsonRpcId,
JsonRpcVersion,
JsonRpcVersion2,
JsonRpcSuccess,
JsonRpcMiddleware,
} from 'json-rpc-engine';
JsonRpcParams,
Json,
} from '@metamask/utils';
import dequal from 'fast-deep-equal';

import messages from './messages';
import {
Expand All @@ -20,7 +21,7 @@ import {

export type UnvalidatedJsonRpcRequest = {
id?: JsonRpcId;
jsonrpc?: JsonRpcVersion;
jsonrpc?: JsonRpcVersion2;
method: string;
params?: unknown;
};
Expand All @@ -37,10 +38,10 @@ export type BaseProviderOptions = {
maxEventListeners?: number;

/**
* `json-rpc-engine` middleware. The middleware will be inserted in the given
* `@metamask/json-rpc-engine` middleware. The middleware will be inserted in the given
* order immediately after engine initialization.
*/
rpcMiddleware?: JsonRpcMiddleware<unknown, unknown>[];
rpcMiddleware?: JsonRpcMiddleware<JsonRpcParams, Json>[];
};

export type RequestArguments = {
Expand Down Expand Up @@ -88,14 +89,14 @@ export abstract class BaseProvider extends SafeEventEmitter {
* The chain ID of the currently connected Ethereum chain.
* See [chainId.network]{@link https://chainid.network} for more information.
*/
public chainId: string | null;
#chainId: string | null;

/**
* The user's currently selected Ethereum address.
* If null, MetaMask is either locked or the user has not permitted any
* addresses to be viewed.
*/
public selectedAddress: string | null;
#selectedAddress: string | null;

/**
* Create a new instance of the provider.
Expand Down Expand Up @@ -123,8 +124,8 @@ export abstract class BaseProvider extends SafeEventEmitter {
};

// Public state
this.selectedAddress = null;
this.chainId = null;
this.#selectedAddress = null;
this.#chainId = null;

// Bind functions to prevent consumers from making unbound calls
this._handleAccountsChanged = this._handleAccountsChanged.bind(this);
Expand All @@ -144,6 +145,18 @@ export abstract class BaseProvider extends SafeEventEmitter {
this._rpcEngine = rpcEngine;
}

//====================
// Public Properties
//====================

get chainId(): string | null {
return this.#chainId;
}

get selectedAddress(): string | null {
return this.#selectedAddress;
}

//====================
// Public Methods
//====================
Expand All @@ -169,7 +182,7 @@ export abstract class BaseProvider extends SafeEventEmitter {
*/
async request<T>(args: RequestArguments): Promise<Maybe<T>> {
if (!args || typeof args !== 'object' || Array.isArray(args)) {
throw ethErrors.rpc.invalidRequest({
throw rpcErrors.invalidRequest({
message: messages.errors.invalidRequestArgs(),
data: args,
});
Expand All @@ -178,7 +191,7 @@ export abstract class BaseProvider extends SafeEventEmitter {
const { method, params } = args;

if (typeof method !== 'string' || method.length === 0) {
throw ethErrors.rpc.invalidRequest({
throw rpcErrors.invalidRequest({
message: messages.errors.invalidRequestMethod(),
data: args,
});
Expand All @@ -189,7 +202,7 @@ export abstract class BaseProvider extends SafeEventEmitter {
!Array.isArray(params) &&
(typeof params !== 'object' || params === null)
) {
throw ethErrors.rpc.invalidRequest({
throw rpcErrors.invalidRequest({
message: messages.errors.invalidRequestParams(),
data: args,
});
Expand Down Expand Up @@ -285,15 +298,9 @@ export abstract class BaseProvider extends SafeEventEmitter {
callback(error, response);
};
}
return this._rpcEngine.handle(
payload as JsonRpcRequest<unknown>,
callbackWrapper,
);
return this._rpcEngine.handle(payload as JsonRpcRequest, callbackWrapper);
}
return this._rpcEngine.handle(
payload as JsonRpcRequest<unknown>[],
callbackWrapper,
);
return this._rpcEngine.handle(payload as JsonRpcRequest[], callbackWrapper);
}

/**
Expand Down Expand Up @@ -331,20 +338,20 @@ export abstract class BaseProvider extends SafeEventEmitter {

let error;
if (isRecoverable) {
error = new EthereumRpcError(
error = new JsonRpcError(
1013, // Try again later
errorMessage ?? messages.errors.disconnected(),
);
this._log.debug(error);
} else {
error = new EthereumRpcError(
error = new JsonRpcError(
1011, // Internal error
errorMessage ?? messages.errors.permanentlyDisconnected(),
);
this._log.error(error);
this.chainId = null;
this.#chainId = null;
this._state.accounts = null;
this.selectedAddress = null;
this.#selectedAddress = null;
this._state.isUnlocked = false;
this._state.isPermanentlyDisconnected = true;
}
Expand Down Expand Up @@ -377,10 +384,10 @@ export abstract class BaseProvider extends SafeEventEmitter {

this._handleConnect(chainId);

if (chainId !== this.chainId) {
this.chainId = chainId;
if (chainId !== this.#chainId) {
this.#chainId = chainId;
if (this._state.initialized) {
this.emit('chainChanged', this.chainId);
this.emit('chainChanged', this.#chainId);
}
}
}
Expand Down Expand Up @@ -433,8 +440,8 @@ export abstract class BaseProvider extends SafeEventEmitter {
this._state.accounts = _accounts as string[];

// handle selectedAddress
if (this.selectedAddress !== _accounts[0]) {
this.selectedAddress = (_accounts[0] as string) || null;
if (this.#selectedAddress !== _accounts[0]) {
this.#selectedAddress = (_accounts[0] as string) || null;
}

// finally, after all state has been updated, emit the event
Expand Down
97 changes: 95 additions & 2 deletions src/MetaMaskInpageProvider.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JsonRpcRequest } from 'json-rpc-engine';
import { JsonRpcRequest } from '@metamask/utils';

import messages from './messages';
import {
Expand Down Expand Up @@ -58,7 +58,7 @@ async function getInitializedProvider({
onMethodCalled?: {
substream: string;
method: string;
callback: (data: JsonRpcRequest<unknown>) => void;
callback: (data: JsonRpcRequest) => void;
}[];
} = {}): Promise<InitializedProviderDetails> {
const onWrite = jest.fn();
Expand Down Expand Up @@ -1070,4 +1070,97 @@ describe('MetaMaskInpageProvider: Miscellanea', () => {
expect(provider.isMetaMask).toBe(true);
});
});

describe('chainId', () => {
let provider: any | MetaMaskInpageProvider;

beforeEach(async () => {
provider = (
await getInitializedProvider({
initialState: {
chainId: '0x5',
},
})
).provider;
});

it('should warn the first time chainId is accessed', async () => {
const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn');

expect(provider.chainId).toBe('0x5');
expect(consoleWarnSpy).toHaveBeenCalledWith(
messages.warnings.chainIdDeprecation,
);
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
});

it('should not allow chainId to be modified', () => {
expect(() => (provider.chainId = '0x539')).toThrow(
'Cannot set property chainId',
);
expect(provider.chainId).toBe('0x5');
});
});

describe('networkVersion', () => {
let provider: any | MetaMaskInpageProvider;

beforeEach(async () => {
provider = (
await getInitializedProvider({
initialState: {
networkVersion: '5',
},
})
).provider;
});

it('should warn the first time networkVersion is accessed', async () => {
const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn');

expect(provider.networkVersion).toBe('5');
expect(consoleWarnSpy).toHaveBeenCalledWith(
messages.warnings.networkVersionDeprecation,
);
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
});

it('should not allow networkVersion to be modified', () => {
expect(() => (provider.networkVersion = '1337')).toThrow(
'Cannot set property networkVersion',
);
expect(provider.networkVersion).toBe('5');
});
});

describe('selectedAddress', () => {
let provider: any | MetaMaskInpageProvider;

beforeEach(async () => {
provider = (
await getInitializedProvider({
initialState: {
accounts: ['0xdeadbeef'],
},
})
).provider;
});

it('should warn the first time selectedAddress is accessed', async () => {
const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn');

expect(provider.selectedAddress).toBe('0xdeadbeef');
expect(consoleWarnSpy).toHaveBeenCalledWith(
messages.warnings.selectedAddressDeprecation,
);
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
});

it('should not allow selectedAddress to be modified', () => {
expect(() => (provider.selectedAddress = '0x12345678')).toThrow(
'Cannot set property selectedAddress',
);
expect(provider.selectedAddress).toBe('0xdeadbeef');
});
});
});
Loading

0 comments on commit 2de3189

Please sign in to comment.