Skip to content

Commit

Permalink
Make chainId, networkVersion, selectedAddress readonly and log …
Browse files Browse the repository at this point in the history
…deprecation warning (#280)

* Make chainId, networkVersion, selectedAddress readonly and log warning

* Fix Proxy getter

* Loosen jest coverage

* Add link to deprecation messages
  • Loading branch information
jiexi authored Sep 11, 2023
1 parent 8bd6311 commit ede28d5
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 23 deletions.
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: 61.29,
functions: 60.24,
lines: 63.82,
statements: 63.97,
branches: 61.9,
functions: 61.79,
lines: 65.18,
statements: 65.3,
},
},

Expand Down
34 changes: 23 additions & 11 deletions src/BaseProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,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 @@ -124,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 @@ -145,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 Down Expand Up @@ -337,9 +349,9 @@ export abstract class BaseProvider extends SafeEventEmitter {
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 @@ -372,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 @@ -428,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
93 changes: 93 additions & 0 deletions src/MetaMaskInpageProvider.test.ts
Original file line number Diff line number Diff line change
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');
});
});
});
52 changes: 44 additions & 8 deletions src/MetaMaskInpageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export type MetaMaskInpageProviderOptions = {
} & Partial<Omit<StreamProviderOptions, 'rpcMiddleware'>>;

type SentWarningsState = {
// properties
chainId: boolean;
networkVersion: boolean;
selectedAddress: boolean;
// methods
enable: boolean;
experimentalMethods: boolean;
Expand All @@ -56,6 +60,10 @@ export const MetaMaskInpageProviderStreamName = 'metamask-provider';

export class MetaMaskInpageProvider extends AbstractStreamProvider {
protected _sentWarnings: SentWarningsState = {
// properties
chainId: false,
networkVersion: false,
selectedAddress: false,
// methods
enable: false,
experimentalMethods: false,
Expand All @@ -76,7 +84,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider {
MetaMaskInpageProvider['_getExperimentalApi']
>;

public networkVersion: string | null;
#networkVersion: string | null;

/**
* Indicating that this provider is a MetaMask provider.
Expand Down Expand Up @@ -118,7 +126,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._initializeStateAsync();

this.networkVersion = null;
this.#networkVersion = null;
this.isMetaMask = true;

this._sendSync = this._sendSync.bind(this);
Expand Down Expand Up @@ -160,6 +168,34 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider {
}
}

//====================
// Deprecated Properties
//====================

get chainId(): string | null {
if (!this._sentWarnings.chainId) {
this._log.warn(messages.warnings.chainIdDeprecation);
this._sentWarnings.chainId = true;
}
return super.chainId;
}

get networkVersion(): string | null {
if (!this._sentWarnings.networkVersion) {
this._log.warn(messages.warnings.networkVersionDeprecation);
this._sentWarnings.networkVersion = true;
}
return this.#networkVersion;
}

get selectedAddress(): string | null {
if (!this._sentWarnings.selectedAddress) {
this._log.warn(messages.warnings.selectedAddressDeprecation);
this._sentWarnings.selectedAddress = true;
}
return super.selectedAddress;
}

//====================
// Public Methods
//====================
Expand Down Expand Up @@ -228,8 +264,8 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider {
*/
protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {
super._handleDisconnect(isRecoverable, errorMessage);
if (this.networkVersion && !isRecoverable) {
this.networkVersion = null;
if (this.#networkVersion && !isRecoverable) {
this.#networkVersion = null;
}
}

Expand Down Expand Up @@ -369,7 +405,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider {
break;

case 'net_version':
result = this.networkVersion ?? null;
result = this.#networkVersion ?? null;
break;

default:
Expand Down Expand Up @@ -457,10 +493,10 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider {
// networkVersion is 'loading'.
super._handleChainChanged({ chainId, networkVersion });

if (this._state.isConnected && networkVersion !== this.networkVersion) {
this.networkVersion = networkVersion as string;
if (this._state.isConnected && networkVersion !== this.#networkVersion) {
this.#networkVersion = networkVersion as string;
if (this._state.initialized) {
this.emit('networkChanged', this.networkVersion);
this.emit('networkChanged', this.#networkVersion);
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/initializeInpageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export function initializeProvider({
const proxiedProvider = new Proxy(provider, {
// some common libraries, e.g. web3@1.x, mess with our API
deleteProperty: () => true,
// fix issue with Proxy unable to access private variables from getters
// https://stackoverflow.com/a/73051482
get(target, propName: 'chainId' | 'networkVersion' | 'selectedAddress') {
return target[propName];
},
});

if (shouldSetOnWindow) {
Expand Down
4 changes: 4 additions & 0 deletions src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ const messages = {
`MetaMask: Connected to chain with ID "${chainId}".`,
},
warnings: {
// deprecated properties
chainIdDeprecation: `MetaMask: 'ethereum.chainId' is deprecated and may be removed in the future. Please use the 'eth_chainId' RPC method instead.\nFor more information, see: https://github.com/MetaMask/metamask-improvement-proposals/discussions/23`,
networkVersionDeprecation: `MetaMask: 'ethereum.networkVersion' is deprecated and may be removed in the future. Please use the 'net_version' RPC method instead.\nFor more information, see: https://github.com/MetaMask/metamask-improvement-proposals/discussions/23`,
selectedAddressDeprecation: `MetaMask: 'ethereum.selectedAddress' is deprecated and may be removed in the future. Please use the 'eth_accounts' RPC method instead.\nFor more information, see: https://github.com/MetaMask/metamask-improvement-proposals/discussions/23`,
// deprecated methods
enableDeprecation: `MetaMask: 'ethereum.enable()' is deprecated and may be removed in the future. Please use the 'eth_requestAccounts' RPC method instead.\nFor more information, see: https://eips.ethereum.org/EIPS/eip-1102`,
sendDeprecation: `MetaMask: 'ethereum.send(...)' is deprecated and may be removed in the future. Please use 'ethereum.sendAsync(...)' or 'ethereum.request(...)' instead.\nFor more information, see: https://eips.ethereum.org/EIPS/eip-1193`,
Expand Down

0 comments on commit ede28d5

Please sign in to comment.