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 Jun 27, 2023
2 parents c7fb13b + f39f84b commit 2056aab
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 67 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [11.1.0]
### Added
- Add warning for callers of `wallet_watchAsset` with ERC721 and ERC1155 token types, that support is currently considered experimental ([#264](https://github.com/MetaMask/providers/pull/264))

## [11.0.0]
### Changed
- **BREAKING**: Minimum Node.js version 16 ([#254](https://github.com/MetaMask/providers/pull/254))
Expand Down Expand Up @@ -220,7 +224,8 @@ 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.0.0...HEAD
[Unreleased]: https://github.com/MetaMask/providers/compare/v11.1.0...HEAD
[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
[10.2.1]: https://github.com/MetaMask/providers/compare/v10.2.0...v10.2.1
[10.2.0]: https://github.com/MetaMask/providers/compare/v10.1.0...v10.2.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: 63.07,
functions: 64.13,
lines: 65.77,
statements: 65.87,
branches: 64.25,
functions: 64.89,
lines: 66.35,
statements: 66.43,
},
},

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metamask/providers",
"version": "11.0.0",
"version": "11.1.0",
"description": "A JavaScript Ethereum provider that connects to the wallet over a stream.",
"keywords": [
"MetaMask",
Expand Down
4 changes: 4 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// TOKEN STANDARDS
export const ERC721 = 'ERC721';
export const ERC1155 = 'ERC1155';
export const ERC20 = 'ERC20';
1 change: 1 addition & 0 deletions src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const messages = {
rpc: {
ethDecryptDeprecation: `MetaMask: The RPC method 'eth_decrypt' is deprecated and may be removed in the future.\nFor more information, see: https://medium.com/metamask/metamask-api-method-deprecation-2b0564a84686`,
ethGetEncryptionPublicKeyDeprecation: `MetaMask: The RPC method 'eth_getEncryptionPublicKey' is deprecated and may be removed in the future.\nFor more information, see: https://medium.com/metamask/metamask-api-method-deprecation-2b0564a84686`,
walletWatchAssetNFTExperimental: `MetaMask: The RPC method 'wallet_watchAsset' is experimental for ERC721/ERC1155 assets and may change in the future.\nFor more information, see: https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-1.md and https://github.com/MetaMask/metamask-improvement-proposals/blob/main/PROCESS-GUIDE.md#proposal-lifecycle`,
},
// misc
experimentalMethods: `MetaMask: 'ethereum._metamask' exposes non-standard, experimental methods. They may be removed or changed without warning.`,
Expand Down
155 changes: 95 additions & 60 deletions src/middleware/createRpcWarningMiddleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,90 +3,123 @@ import { JsonRpcEngine, JsonRpcFailure, JsonRpcSuccess } from 'json-rpc-engine';
import { createRpcWarningMiddleware } from './createRpcWarningMiddleware';
import messages from '../messages';

const warnings = [
const affected = [
{
scenario: 'eth_decrypt',
method: 'eth_decrypt',
warning: messages.warnings.rpc.ethDecryptDeprecation,
},
{
scenario: 'eth_getEncryptionPublicKey',
method: 'eth_getEncryptionPublicKey',
warning: messages.warnings.rpc.ethGetEncryptionPublicKeyDeprecation,
},
{
scenario: 'wallet_watchAsset with `type: "ERC721"`',
method: 'wallet_watchAsset',
params: {
type: 'ERC721',
options: {},
},
warning: messages.warnings.rpc.walletWatchAssetNFTExperimental,
},
{
scenario: 'wallet_watchAsset with `type: "ERC1155"`',
method: 'wallet_watchAsset',
params: {
type: 'ERC1155',
options: {},
},
warning: messages.warnings.rpc.walletWatchAssetNFTExperimental,
},
];

const unaffected = [
{
scenario: 'eth_chainId',
method: 'eth_chainId',
},
{
scenario: 'wallet_watchAsset with `type: "ERC20"`',
method: 'wallet_watchAsset',
params: {
type: 'ERC20',
options: {},
},
},
];

describe('createRpcWarningMiddleware', () => {
for (const { method, warning } of warnings) {
describe(`${method}`, () => {
it('should warn the first time the method is called', async () => {
const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn');
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);

await engine.handle({ jsonrpc: '2.0', id: 1, method });

expect(consoleWarnSpy).toHaveBeenCalledWith(warning);
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
});
describe.each(affected)('$scenario', ({ method, params = {}, warning }) => {
it('should warn the first time the method is called', async () => {
const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn');
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);

it('should not warn the second time the method is called', async () => {
const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn');
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);
await engine.handle({ jsonrpc: '2.0', id: 1, method, params });

await engine.handle({ jsonrpc: '2.0', id: 1, method });
await engine.handle({ jsonrpc: '2.0', id: 1, method });
expect(consoleWarnSpy).toHaveBeenCalledWith(warning);
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
});

expect(consoleWarnSpy).toHaveBeenCalledWith(warning);
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
});
it('should not warn the second time the method is called', async () => {
const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn');
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);

it('should allow the method to succeed', async () => {
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);
engine.push((_req, res, _next, end) => {
res.result = 'success!';
end();
});

const response = (await engine.handle({
jsonrpc: '2.0',
id: 1,
method,
})) as JsonRpcSuccess<unknown>;

expect(response.result).toBe('success!');
await engine.handle({ jsonrpc: '2.0', id: 1, method, params });
await engine.handle({ jsonrpc: '2.0', id: 1, method, params });

expect(consoleWarnSpy).toHaveBeenCalledWith(warning);
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
});

it('should allow the method to succeed', async () => {
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);
engine.push((_req, res, _next, end) => {
res.result = 'success!';
end();
});

it('should allow the method to fail', async () => {
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);
engine.push(() => {
throw new Error('Failure!');
});

const result = (await engine.handle({
jsonrpc: '2.0',
id: 1,
method,
})) as JsonRpcFailure;

expect(result.error.message).toBe('Failure!');
const response = (await engine.handle({
jsonrpc: '2.0',
id: 1,
method,
})) as JsonRpcSuccess<unknown>;

expect(response.result).toBe('success!');
});

it('should allow the method to fail', async () => {
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);
engine.push(() => {
throw new Error('Failure!');
});

const result = (await engine.handle({
jsonrpc: '2.0',
id: 1,
method,
})) as JsonRpcFailure;

expect(result.error.message).toBe('Failure!');
});
}
});

describe('unaffected method', () => {
describe.each(unaffected)('$scenario', ({ method, params = {} }) => {
it('should not issue a warning', async () => {
const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn');
const middleware = createRpcWarningMiddleware(globalThis.console);
const engine = new JsonRpcEngine();
engine.push(middleware);

await engine.handle({ jsonrpc: '2.0', id: 1, method: 'eth_chainId' });
await engine.handle({ jsonrpc: '2.0', id: 1, method, params });

expect(consoleWarnSpy).not.toHaveBeenCalled();
});
Expand All @@ -103,7 +136,8 @@ describe('createRpcWarningMiddleware', () => {
const response = (await engine.handle({
jsonrpc: '2.0',
id: 1,
method: 'eth_chainId',
method,
params,
})) as JsonRpcSuccess<unknown>;

expect(response.result).toBe('success!');
Expand All @@ -120,7 +154,8 @@ describe('createRpcWarningMiddleware', () => {
const result = (await engine.handle({
jsonrpc: '2.0',
id: 1,
method: 'eth_chainId',
method,
params,
})) as JsonRpcFailure;

expect(result.error.message).toBe('Failure!');
Expand Down
13 changes: 12 additions & 1 deletion src/middleware/createRpcWarningMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { JsonRpcMiddleware } from 'json-rpc-engine';
import type { JsonRpcMiddleware, JsonRpcRequest } from 'json-rpc-engine';

import { ERC1155, ERC721 } from '../constants';
import messages from '../messages';
import type { ConsoleLike } from '../utils';

Expand All @@ -15,6 +16,7 @@ export function createRpcWarningMiddleware(
const sentWarnings = {
ethDecryptDeprecation: false,
ethGetEncryptionPublicKeyDeprecation: false,
walletWatchAssetNFTExperimental: false,
};

return (req, _res, next) => {
Expand All @@ -27,6 +29,15 @@ export function createRpcWarningMiddleware(
) {
log.warn(messages.warnings.rpc.ethGetEncryptionPublicKeyDeprecation);
sentWarnings.ethGetEncryptionPublicKeyDeprecation = true;
} else if (
!sentWarnings.walletWatchAssetNFTExperimental &&
req.method === 'wallet_watchAsset' &&
[ERC721, ERC1155].includes(
(req as JsonRpcRequest<{ type: string }>).params?.type || '',
)
) {
log.warn(messages.warnings.rpc.walletWatchAssetNFTExperimental);
sentWarnings.walletWatchAssetNFTExperimental = true;
}
next();
};
Expand Down

0 comments on commit 2056aab

Please sign in to comment.