Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add onNameLookup export #1394

Merged
merged 61 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
bf3c3cf
added new handler type and updated enums for the new export handler
hmalik88 May 9, 2023
8e8524d
updated utils for name lookup
hmalik88 May 9, 2023
eb74ebb
updated onNameLookupResponse type
hmalik88 May 9, 2023
85c5543
removed onNameLookup caveat
hmalik88 May 9, 2023
518efc0
added name-lookup endowment and updated tests
hmalik88 May 9, 2023
89fcab7
updated base snap executor to account for new export
hmalik88 May 9, 2023
7b7e1a2
fix coverage
hmalik88 May 9, 2023
9d0e0b6
another fix
hmalik88 May 9, 2023
6d28835
update permissions struct
hmalik88 May 9, 2023
135732f
fix jsdoc
hmalik88 May 10, 2023
ba5aea3
update jsdoc
hmalik88 May 10, 2023
2af6085
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Jun 9, 2023
01f8ad3
updated tests and brought branch up to date
hmalik88 Jun 9, 2023
a199562
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Jun 14, 2023
6e873dc
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Jun 15, 2023
92968e5
made changes per comments
hmalik88 Jun 15, 2023
a61d7b7
fixes
hmalik88 Jun 15, 2023
8fe0d66
fix base snap executor test
hmalik88 Jun 16, 2023
b124891
update
hmalik88 Jun 30, 2023
56a27dd
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Jun 30, 2023
c04ae3d
fix handler type
hmalik88 Jun 30, 2023
a698eae
update shasums
hmalik88 Jun 30, 2023
9335c83
fix test and update coverage
hmalik88 Jun 30, 2023
6ef9ea5
fix coverage again
hmalik88 Jun 30, 2023
dbfdf5c
clear cache
hmalik88 Jun 30, 2023
458c2a5
update types
hmalik88 Jul 31, 2023
d67c258
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Sep 1, 2023
afb1e8d
fix coverage
hmalik88 Sep 1, 2023
87c89d6
add name lookup to exports
hmalik88 Sep 1, 2023
7763b15
update coverage and shasums
hmalik88 Sep 1, 2023
a9cb007
update ee coverage
hmalik88 Sep 1, 2023
44f3a15
fix snapshot
hmalik88 Sep 1, 2023
d4e3719
fix test
hmalik88 Sep 1, 2023
4a737c1
fix coverage
hmalik88 Sep 2, 2023
2213cd0
fix struct and add test
hmalik88 Sep 2, 2023
5e6a96c
update ee coverage
hmalik88 Sep 2, 2023
f128f80
actually fix type
hmalik88 Sep 3, 2023
921fe28
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Sep 7, 2023
e2974f1
addressed PR comments
hmalik88 Sep 7, 2023
d122d61
fix coverage
hmalik88 Sep 7, 2023
a9700de
fix test description
hmalik88 Sep 8, 2023
d8c056d
added chainIds caveat
hmalik88 Sep 8, 2023
2f7e5fa
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Sep 8, 2023
76f110c
update shasums
hmalik88 Sep 8, 2023
7912dd0
update snap-utils coverage
hmalik88 Sep 8, 2023
af9d171
fix tests
hmalik88 Sep 8, 2023
65cc62d
test fixes
hmalik88 Sep 8, 2023
606db17
fix condition (again)
hmalik88 Sep 8, 2023
3c047cc
update coverage
hmalik88 Sep 9, 2023
889f60c
fix branches coverage
hmalik88 Sep 9, 2023
04e1095
remove cast
hmalik88 Sep 9, 2023
b421771
moved test
hmalik88 Sep 9, 2023
3e5015f
fix test and update coverage
hmalik88 Sep 9, 2023
b3db22b
added new type and updated casting
hmalik88 Sep 10, 2023
b34d612
added extra validation and added more tests
hmalik88 Sep 12, 2023
64334a7
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Sep 12, 2023
941d0c6
update shasums
hmalik88 Sep 12, 2023
252891c
actually fix shasums
hmalik88 Sep 12, 2023
d7f40aa
fix coverage
hmalik88 Sep 12, 2023
9b73c24
Merge branch 'main' into hm/cypherpunk-address-resolution
hmalik88 Sep 12, 2023
5920bd3
updated shasums (again)
hmalik88 Sep 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/snaps-controllers/coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"branches": 89.8,
"functions": 95.28,
"lines": 96.53,
"statements": 96.17
"functions": 95.31,
"lines": 96.54,
"statements": 96.19
}
Original file line number Diff line number Diff line change
Expand Up @@ -2523,7 +2523,7 @@ describe('SnapController', () => {
[MOCK_SNAP_ID]: {},
}),
).rejects.toThrow(
'A snap must request at least one of the following permissions: endowment:rpc, endowment:keyring, endowment:transaction-insight, endowment:cronjob.',
'A snap must request at least one of the following permissions: endowment:rpc, endowment:keyring, endowment:transaction-insight, endowment:cronjob, endowment:name-lookup.',
);

controller.destroy();
Expand Down
1 change: 1 addition & 0 deletions packages/snaps-controllers/src/snaps/endowments/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export enum SnapEndowments {
EthereumProvider = 'endowment:ethereum-provider',
Rpc = 'endowment:rpc',
WebAssemblyAccess = 'endowment:webassembly',
NameLookup = 'endowment:name-lookup',
}
3 changes: 3 additions & 0 deletions packages/snaps-controllers/src/snaps/endowments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getKeyringCaveatMapper,
} from './keyring';
import { longRunningEndowmentBuilder } from './long-running';
import { nameLookupEndowmentBuilder } from './name-lookup';
import { networkAccessEndowmentBuilder } from './network-access';
import {
getRpcCaveatMapper,
Expand All @@ -38,6 +39,7 @@ export const endowmentPermissionBuilders = {
ethereumProviderEndowmentBuilder,
[rpcEndowmentBuilder.targetName]: rpcEndowmentBuilder,
[webAssemblyEndowmentBuilder.targetName]: webAssemblyEndowmentBuilder,
[nameLookupEndowmentBuilder.targetName]: nameLookupEndowmentBuilder,
} as const;

export const endowmentCaveatSpecifications = {
Expand All @@ -63,6 +65,7 @@ export const handlerEndowments: Record<HandlerType, string> = {
[HandlerType.SnapKeyring]: keyringEndowmentBuilder.targetName,
[HandlerType.OnTransaction]: transactionInsightEndowmentBuilder.targetName,
[HandlerType.OnCronjob]: cronjobEndowmentBuilder.targetName,
[HandlerType.OnNameLookup]: nameLookupEndowmentBuilder.targetName,
};

export * from './enum';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { PermissionType, SubjectType } from '@metamask/permission-controller';

import { SnapEndowments } from '.';
import { nameLookupEndowmentBuilder } from './name-lookup';

describe('endowment:name-lookup', () => {
const specification = nameLookupEndowmentBuilder.specificationBuilder({});
it('builds the expected permission specification', () => {
expect(specification).toStrictEqual({
permissionType: PermissionType.Endowment,
targetName: SnapEndowments.NameLookup,
endowmentGetter: expect.any(Function),
allowedCaveats: null,
subjectTypes: [SubjectType.Snap],
});

expect(specification.endowmentGetter()).toBeUndefined();
});
});
45 changes: 45 additions & 0 deletions packages/snaps-controllers/src/snaps/endowments/name-lookup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
EndowmentGetterParams,
PermissionType,
PermissionSpecificationBuilder,
ValidPermissionSpecification,
SubjectType,
} from '@metamask/permission-controller';
import { NonEmptyArray } from '@metamask/utils';

import { SnapEndowments } from './enum';

const permissionName = SnapEndowments.NameLookup;

type NameLookupEndowmentSpecification = ValidPermissionSpecification<{
permissionType: PermissionType.Endowment;
targetName: typeof permissionName;
endowmentGetter: (_options?: EndowmentGetterParams) => undefined;
allowedCaveats: Readonly<NonEmptyArray<string>> | null;
}>;

/**
* `endowment:name-lookup` returns nothing; it is intended to be used as a flag
* by the extension to detect whether the snap has the capability to resolve content entered into the send screen.
hmalik88 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param _builderOptions - Optional specification builder options.
* @returns The specification for the name-lookup endowment.
*/
const specificationBuilder: PermissionSpecificationBuilder<
PermissionType.Endowment,
any,
NameLookupEndowmentSpecification
> = (_builderOptions?: unknown) => {
return {
permissionType: PermissionType.Endowment,
targetName: permissionName,
allowedCaveats: null,
endowmentGetter: (_getterOptions?: EndowmentGetterParams) => undefined,
subjectTypes: [SubjectType.Snap],
};
};

export const nameLookupEndowmentBuilder = Object.freeze({
targetName: permissionName,
specificationBuilder,
} as const);
9 changes: 9 additions & 0 deletions packages/snaps-controllers/src/snaps/permission.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ describe('buildSnapEndowmentSpecifications', () => {
],
"targetName": "endowment:long-running",
},
"endowment:name-lookup": {
"allowedCaveats": null,
"endowmentGetter": [Function],
"permissionType": "Endowment",
"subjectTypes": [
"snap",
],
"targetName": "endowment:name-lookup",
},
"endowment:network-access": {
"allowedCaveats": null,
"endowmentGetter": [Function],
Expand Down
8 changes: 4 additions & 4 deletions packages/snaps-execution-environments/coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"branches": 79.31,
"functions": 92.14,
"lines": 91.17,
"statements": 91.03
"branches": 79.45,
"functions": 91.48,
"lines": 90.93,
"statements": 90.66
}
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,41 @@ describe('BaseSnapExecutor', () => {
});
});

it('supports onNameLookup export', async () => {
const CODE = `module.exports.onNameLookup = ({ id, content }) => content`;

const executor = new TestSnapExecutor();
await executor.executeSnap(1, MOCK_SNAP_ID, CODE, []);

expect(await executor.readCommand()).toStrictEqual({
jsonrpc: '2.0',
id: 1,
result: 'OK',
});

await executor.writeCommand({
jsonrpc: '2.0',
id: 2,
method: 'snapRpc',
params: [
MOCK_SNAP_ID,
HandlerType.OnNameLookup,
MOCK_ORIGIN,
{
jsonrpc: '2.0',
method: 'foo',
params: { id: 'eip155:1', content: 'foo.lens' },
},
],
});

expect(await executor.readCommand()).toStrictEqual({
id: 2,
jsonrpc: '2.0',
result: 'foo.lens',
});
});

it('blocks snaps from escaping confinement by using unbound this', async () => {
const consoleSpy = spy(console, 'log');

Expand Down
10 changes: 9 additions & 1 deletion packages/snaps-execution-environments/src/common/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { assertExhaustive } from '@metamask/utils';
import { InvokeSnap, InvokeSnapArgs } from './BaseSnapExecutor';
import {
assertIsOnTransactionRequestArguments,
assertIsOnNameLookupRequestArguments,
ExecuteSnap,
JsonRpcRequestWithoutId,
Ping,
Expand Down Expand Up @@ -44,7 +45,14 @@ export function getHandlerArguments(
transactionOrigin,
};
}

case HandlerType.OnNameLookup: {
assertIsOnNameLookupRequestArguments(request.params);
const { id, content } = request.params;
return {
id,
content,
};
}
case HandlerType.OnRpcRequest:
case HandlerType.SnapKeyring:
return { origin, request };
Expand Down
28 changes: 28 additions & 0 deletions packages/snaps-execution-environments/src/common/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const VALIDATION_FUNCTIONS = {
[HandlerType.OnTransaction]: validateFunctionExport,
[HandlerType.SnapKeyring]: validateKeyringExport,
[HandlerType.OnCronjob]: validateFunctionExport,
[HandlerType.OnNameLookup]: validateFunctionExport,
};

/**
Expand Down Expand Up @@ -178,6 +179,33 @@ export function assertIsOnTransactionRequestArguments(
);
}

export const OnNameLookupRequestArgumentsStruct = object({
id: ChainIdStruct,
hmalik88 marked this conversation as resolved.
Show resolved Hide resolved
content: string(),
hmalik88 marked this conversation as resolved.
Show resolved Hide resolved
});

export type OnNameLookupRequestArguments = Infer<
typeof OnNameLookupRequestArgumentsStruct
>;

/**
* Asserts that the given value is a valid {@link OnNameLookupRequestArguments}
* object.
*
* @param value - The value to validate.
* @throws If the value is not a valid {@link OnNameLookupRequestArguments}
* object.
*/
export function assertIsOnNameLookupRequestArguments(
value: unknown,
): asserts value is OnNameLookupRequestArguments {
assertStruct(
value,
OnNameLookupRequestArgumentsStruct,
'Invalid request params',
);
}

const OkResponseStruct = assign(
JsonRpcSuccessStruct,
object({
Expand Down
4 changes: 2 additions & 2 deletions packages/snaps-utils/coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"branches": 94.14,
"functions": 100,
"lines": 98.5,
"statements": 94.64
"lines": 98.51,
"statements": 94.41
}
35 changes: 34 additions & 1 deletion packages/snaps-utils/src/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Component } from '@metamask/snaps-ui';
import { Json, JsonRpcRequest } from '@metamask/utils';

import { AccountId, ChainId, RequestArguments } from './namespace';
import {
AccountId,
AccountAddress,
ChainId,
Caip2ChainId,
RequestArguments,
} from './namespace';

/**
* The `onRpcRequest` handler. This is called whenever a JSON-RPC request is
Expand Down Expand Up @@ -68,6 +74,32 @@ export type OnCronjobHandler<
| undefined,
> = (args: { request: JsonRpcRequest<Params> }) => Promise<unknown>;

/**
* The response from a snap's `onNameLookup` handler.
*
* @property protocolName - This refers to the protocol that was used for the address lookup.
hmalik88 marked this conversation as resolved.
Show resolved Hide resolved
*
* If the snap has no resolved addresses from its lookup, this should be `null`.
*/
export type OnNameLookupResponse = {
resolvedAccount: AccountAddress;
} | null;

/**
* The `onNameLookup` handler. This is called whenever content is entered
* into the send to field for sending assets to an EOA address.
*
* @param args - The request arguments.
* @param args.content - The human-readable address that is to be resolved.
hmalik88 marked this conversation as resolved.
Show resolved Hide resolved
* @param args.id - The CAIP-2 chain ID of the network the transaction is
* being submitted to.
* @returns Resolved address(es) from the content lookup. See {@link OnNameLookupResponse}.
*/
export type OnNameLookupHandler = (args: {
id: Caip2ChainId;
content: string;
}) => Promise<OnNameLookupResponse>;

/**
* A request sent to the `handleRequest` handler of a snap keyring.
*
Expand Down Expand Up @@ -115,6 +147,7 @@ export type SnapFunctionExports = {
onRpcRequest?: OnRpcRequestHandler;
onTransaction?: OnTransactionHandler;
onCronjob?: OnCronjobHandler;
onNameLookup?: OnNameLookupHandler;
};

/**
Expand Down
1 change: 1 addition & 0 deletions packages/snaps-utils/src/manifest/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export const PermissionsStruct = type({
}),
),
wallet_snap: optional(SnapIdsStruct),
'endowment:name-lookup': optional(object({})),
hmalik88 marked this conversation as resolved.
Show resolved Hide resolved
});
/* eslint-enable @typescript-eslint/naming-convention */

Expand Down
5 changes: 5 additions & 0 deletions packages/snaps-utils/src/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export const CHAIN_ID_REGEX =
export const ACCOUNT_ID_REGEX =
/^(?<chainId>(?<namespace>[-a-z0-9]{3,8}):(?<reference>[-a-zA-Z0-9]{1,32})):(?<accountAddress>[a-zA-Z0-9]{1,64})$/u;

export const ACCOUNT_ADDRESS_REGEX = /^(?<accountAddress>[a-zA-Z0-9]{1,64})$/u;
hmalik88 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Parse a chain ID string to an object containing the namespace and reference.
* This validates the chain ID before parsing it.
Expand Down Expand Up @@ -85,11 +87,14 @@ export const LimitedString = size(string(), 1, 40);
*/
export const ChainIdStruct = pattern(string(), CHAIN_ID_REGEX);
export type ChainId = `${string}:${string}`;
export type Caip2ChainId = Infer<typeof ChainIdStruct>;
hmalik88 marked this conversation as resolved.
Show resolved Hide resolved

export const AccountIdStruct = pattern(string(), ACCOUNT_ID_REGEX);
export type AccountId = `${ChainId}:${string}`;

export const AccountIdArrayStruct = array(AccountIdStruct);
export const AccountAddressStruct = pattern(string(), ACCOUNT_ADDRESS_REGEX);
export type AccountAddress = Infer<typeof AccountAddressStruct>;

/**
* A chain descriptor.
Expand Down
1 change: 1 addition & 0 deletions packages/snaps-utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export enum HandlerType {
OnTransaction = 'onTransaction',
SnapKeyring = 'keyring',
OnCronjob = 'onCronjob',
OnNameLookup = 'onNameLookup',
}

export const SNAP_EXPORT_NAMES = Object.values(HandlerType);
Expand Down