Skip to content

Commit

Permalink
feat: add missing wallet_requestPermissions and `wallet_getPermissi…
Browse files Browse the repository at this point in the history
…ons` to the mobile API. (#6732)

* feat: added missing requestPermissions and getPermissions API methods

* fix: types for requestPermissions and getPermissions implementation

* fix: upgrade permission controller to get tuple typing fix

* fix: linting

* Fixed unit tests for permissions controller

* Fixed yarn.lock version
  • Loading branch information
shanejonas authored Jul 20, 2023
1 parent 9dc6e95 commit d3b4046
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 15 deletions.
2 changes: 1 addition & 1 deletion app/core/Permissions/specifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const getCaveatSpecifications = ({ getIdentities }) => ({
export const getPermissionSpecifications = ({ getAllAccounts }) => ({
[PermissionKeys.eth_accounts]: {
permissionType: PermissionType.RestrictedMethod,
targetKey: PermissionKeys.eth_accounts,
targetName: PermissionKeys.eth_accounts,
allowedCaveats: [CaveatTypes.restrictReturnedAccounts],

factory: (permissionOptions, requestData) => {
Expand Down
2 changes: 1 addition & 1 deletion app/core/Permissions/specifications.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ describe('PermissionController specifications', () => {
const permissionSpecifications = getPermissionSpecifications({});
expect(Object.keys(permissionSpecifications)).toHaveLength(1);
expect(
permissionSpecifications[RestrictedMethods.eth_accounts].targetKey,
permissionSpecifications[RestrictedMethods.eth_accounts].targetName,
).toStrictEqual(RestrictedMethods.eth_accounts);
});

Expand Down
66 changes: 66 additions & 0 deletions app/core/RPCMethods/RPCMethodMiddleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getPermittedAccounts } from '../Permissions';
import { RPC } from '../../constants/network';
import { getRpcMethodMiddleware } from './RPCMethodMiddleware';
import AppConstants from '../AppConstants';
import { PermissionConstraint } from '@metamask/permission-controller';

jest.mock('../Engine', () => ({
context: {
Expand All @@ -32,13 +33,18 @@ jest.mock('../Engine', () => ({
newUnsignedPersonalMessage: jest.fn(),
newUnsignedTypedMessage: jest.fn(),
},
PermissionController: {
requestPermissions: jest.fn(),
getPermissions: jest.fn(),
},
},
}));
const MockEngine = Engine as Omit<typeof Engine, 'context'> & {
context: {
NetworkController: Record<string, any>;
PreferencesController: Record<string, any>;
TransactionController: Record<string, any>;
PermissionController: Record<string, any>;
};
};

Expand Down Expand Up @@ -416,6 +422,66 @@ describe('getRpcMethodMiddleware', () => {
});
}

describe('wallet_requestPermissions', () => {
it('can requestPermissions for eth_accounts', async () => {
MockEngine.context.PermissionController.requestPermissions.mockImplementation(
async () => [
{
eth_accounts: {
parentCapability: 'eth_accounts',
caveats: [],
},
},
],
);
const middleware = getRpcMethodMiddleware({
...getMinimalOptions(),
hostname: 'example.metamask.io',
});
const request = {
jsonrpc,
id: 1,
method: 'wallet_requestPermissions',
params: [
{
eth_accounts: {},
},
],
};
const response = await callMiddleware({ middleware, request });
expect(
(response as JsonRpcSuccess<PermissionConstraint[]>).result,
).toEqual([{ parentCapability: 'eth_accounts', caveats: [] }]);
});
});

describe('wallet_getPermissions', () => {
it('can getPermissions', async () => {
MockEngine.context.PermissionController.getPermissions.mockImplementation(
() => ({
eth_accounts: {
parentCapability: 'eth_accounts',
caveats: [],
},
}),
);
const middleware = getRpcMethodMiddleware({
...getMinimalOptions(),
hostname: 'example.metamask.io',
});
const request = {
jsonrpc,
id: 1,
method: 'wallet_getPermissions',
params: [],
};
const response = await callMiddleware({ middleware, request });
expect(
(response as JsonRpcSuccess<PermissionConstraint[]>).result,
).toEqual([{ parentCapability: 'eth_accounts', caveats: [] }]);
});
});

describe('eth_sendTransaction', () => {
describe('browser', () => {
it('sends the transaction and returns the resulting hash', async () => {
Expand Down
51 changes: 50 additions & 1 deletion app/core/RPCMethods/RPCMethodMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Alert } from 'react-native';
import { getVersion } from 'react-native-device-info';
import { createAsyncMiddleware } from 'json-rpc-engine';
import {
createAsyncMiddleware,
JsonRpcEngineCallbackError,
} from 'json-rpc-engine';
import { ethErrors } from 'eth-json-rpc-errors';
import {
EndFlowOptions,
Expand All @@ -11,6 +14,7 @@ import { recoverPersonalSignature } from '@metamask/eth-sig-util';
import RPCMethods from './index.js';
import { RPC } from '../../constants/network';
import { NetworksChainId, NetworkType } from '@metamask/controller-utils';
import { permissionRpcMethods } from '@metamask/permission-controller';
import Networks, {
blockTagParamIndex,
getAllNetworks,
Expand Down Expand Up @@ -248,7 +252,52 @@ export const getRpcMethodMiddleware = ({
return responseData;
};

const [requestPermissionsHandler, getPermissionsHandler] =
permissionRpcMethods.handlers;

const rpcMethods: any = {
wallet_getPermissions: async () =>
new Promise<any>((resolve) => {
getPermissionsHandler.implementation(
req,
res,
next,
() => {
resolve(undefined);
},
{
getPermissionsForOrigin:
Engine.context.PermissionController.getPermissions.bind(
Engine.context.PermissionController,
hostname,
),
},
);
}),
wallet_requestPermissions: async () =>
new Promise<any>((resolve, reject) => {
requestPermissionsHandler
.implementation(
req,
res,
next,
(err: JsonRpcEngineCallbackError | undefined) => {
if (err) {
return reject(err);
}
resolve(undefined);
},
{
requestPermissionsForOrigin:
Engine.context.PermissionController.requestPermissions.bind(
Engine.context.PermissionController,
{ origin: hostname },
),
},
)
?.then(resolve)
.catch(reject);
}),
eth_getTransactionByHash: async () => {
res.result = await polyfillGasPrice('getTransactionByHash', req.params);
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
"@metamask/gas-fee-controller": "4.0.0",
"@metamask/keyring-controller": "^1.0.1",
"@metamask/network-controller": "^5.0.0",
"@metamask/permission-controller": "~3.0.0",
"@metamask/permission-controller": "^4.0.1",
"@metamask/phishing-controller": "^3.0.0",
"@metamask/preferences-controller": "^2.1.0",
"@metamask/sdk-communication-layer": "^0.5.0",
Expand Down
22 changes: 11 additions & 11 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5110,7 +5110,7 @@
"@metamask/base-controller" "^2.0.0"
"@metamask/controller-utils" "^3.0.0"

"@metamask/approval-controller@3.4.0", "@metamask/approval-controller@^2.0.0", "@metamask/approval-controller@^2.1.1", "@metamask/approval-controller@^3.4.0":
"@metamask/approval-controller@3.4.0", "@metamask/approval-controller@^2.1.1", "@metamask/approval-controller@^3.3.0", "@metamask/approval-controller@^3.4.0":
version "3.4.0"
resolved "https://registry.yarnpkg.com/@metamask/approval-controller/-/approval-controller-3.4.0.tgz#282900361d42f785578728b45014ff8cb5e557ea"
integrity sha512-DjqrhiX9+W/Fh6Crr7FPJ87Y/uhPWzBvfXGtekv1LHZNmEtUxkrA7aelddUM0fpTdURIGT4aNGBoQudFidc+Lw==
Expand Down Expand Up @@ -5195,7 +5195,7 @@
resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-2.2.0.tgz#277764d0d56e37180ae7644a9d11eb96295b36fc"
integrity sha512-SM6A4C7vXNbVpgMTX67kfW8QWvu3eSXxMZlY5PqZBTkvri1s9zgQ0uwRkK5r2VXNEoVmXCDnnEX/tX5EzzgNUQ==

"@metamask/controller-utils@^1.0.0", "@metamask/controller-utils@^3.0.0", "@metamask/controller-utils@^3.4.0", "@metamask/controller-utils@~3.0.0":
"@metamask/controller-utils@^1.0.0", "@metamask/controller-utils@^3.0.0", "@metamask/controller-utils@^3.4.0", "@metamask/controller-utils@^4.0.1", "@metamask/controller-utils@~3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@metamask/controller-utils/-/controller-utils-3.0.0.tgz#e0984cdab14280409297671b5858891527c5e4ee"
integrity sha512-JjFWBZnnh5DSX2tRsw5xtXxaqVkTzaW7mkSZ+lL3LoCAw47Cf8zGP1kGR6VKxcceKi+MpEFvZr7gf1OFnOoEjw==
Expand Down Expand Up @@ -5378,18 +5378,18 @@
"@metamask/safe-event-emitter" "^2.0.0"
through2 "^2.0.3"

"@metamask/permission-controller@~3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@metamask/permission-controller/-/permission-controller-3.0.0.tgz#2c58f749c4fc192743fd2e5fad5ffa503ba1c068"
integrity sha512-902jw48yetCsNo6DGrXKHDWWz/QzdmC90O6Am5WgUmwUlboU9Mr0uS8Fp8b7qPQiDZRXz8PvckodyEX2XNW+tQ==
"@metamask/permission-controller@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@metamask/permission-controller/-/permission-controller-4.0.1.tgz#b3eee18b49cd9be9e7241e1038fd0df30d546867"
integrity sha512-LMhD2+3QHmrYQzs81/ZUf473or+B+HmKLpVwpC0L1FfAeSrEpcDrfxWbGtYwiMo9wqNqwN2UAH7ifNy5rudtsA==
dependencies:
"@metamask/approval-controller" "^2.0.0"
"@metamask/base-controller" "^2.0.0"
"@metamask/controller-utils" "^3.0.0"
"@metamask/types" "^1.1.0"
"@metamask/approval-controller" "^3.3.0"
"@metamask/base-controller" "^3.0.0"
"@metamask/controller-utils" "^4.0.1"
"@metamask/utils" "^5.0.2"
"@types/deep-freeze-strict" "^1.1.0"
deep-freeze-strict "^1.1.1"
eth-rpc-errors "^4.0.0"
eth-rpc-errors "^4.0.2"
immer "^9.0.6"
json-rpc-engine "^6.1.0"
nanoid "^3.1.31"
Expand Down

0 comments on commit d3b4046

Please sign in to comment.