Skip to content
This repository has been archived by the owner on Oct 7, 2024. It is now read-only.

Commit

Permalink
feat!: add chainId to user ops
Browse files Browse the repository at this point in the history
  • Loading branch information
ccharly committed Mar 15, 2024
1 parent 2faaaac commit 7f43cda
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
43 changes: 34 additions & 9 deletions src/SnapKeyring.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
EthUserOperation,
EthUserOperationPatch,
KeyringAccount,
KeyringExecutionContext,
} from '@metamask/keyring-api';
import { EthAccountType, EthMethod } from '@metamask/keyring-api';
import { KeyringEvent } from '@metamask/keyring-api/dist/events';
Expand All @@ -13,6 +14,7 @@ import type { SnapId } from '@metamask/snaps-sdk';

import type { KeyringState } from '.';
import { SnapKeyring } from '.';
import { CaipNamespaces, toCaipChainId } from './caip';

const regexForUUIDInRequiredSyncErrorMessage =
/Request '[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}' to snap 'local:snap.mock' is pending and expectSync is true/u;
Expand Down Expand Up @@ -59,6 +61,10 @@ describe('SnapKeyring', () => {
},
] as const;

const keyringContext: KeyringExecutionContext = {
chainId: '1',
};

beforeEach(async () => {
keyring = new SnapKeyring(
mockSnapController as unknown as SnapController,
Expand Down Expand Up @@ -908,6 +914,7 @@ describe('SnapKeyring', () => {
const baseUserOp = await keyring.prepareUserOperation(
accounts[0].address,
baseTxs,
keyringContext,
);

expect(mockSnapController.handleRequest).toHaveBeenCalledWith({
Expand All @@ -920,7 +927,7 @@ describe('SnapKeyring', () => {
method: 'keyring_submitRequest',
params: {
id: expect.any(String),
scope: expect.any(String),
scope: toCaipChainId(CaipNamespaces.Eip155, keyringContext.chainId),
account: accounts[0].id,
request: {
method: 'eth_prepareUserOperation',
Expand Down Expand Up @@ -960,6 +967,7 @@ describe('SnapKeyring', () => {
const patch = await keyring.patchUserOperation(
accounts[0].address,
userOp,
keyringContext,
);

expect(mockSnapController.handleRequest).toHaveBeenCalledWith({
Expand All @@ -972,7 +980,7 @@ describe('SnapKeyring', () => {
method: 'keyring_submitRequest',
params: {
id: expect.any(String),
scope: expect.any(String),
scope: toCaipChainId(CaipNamespaces.Eip155, keyringContext.chainId),
account: accounts[0].id,
request: {
method: 'eth_patchUserOperation',
Expand Down Expand Up @@ -1008,6 +1016,7 @@ describe('SnapKeyring', () => {
const signature = await keyring.signUserOperation(
accounts[0].address,
userOp,
keyringContext,
);

expect(mockSnapController.handleRequest).toHaveBeenCalledWith({
Expand All @@ -1020,7 +1029,7 @@ describe('SnapKeyring', () => {
method: 'keyring_submitRequest',
params: {
id: expect.any(String),
scope: expect.any(String),
scope: toCaipChainId(CaipNamespaces.Eip155, keyringContext.chainId),
account: accounts[0].id,
request: {
method: 'eth_signUserOperation',
Expand Down Expand Up @@ -1220,7 +1229,11 @@ describe('SnapKeyring', () => {
result: mockExpectedUserOp,
});

await keyring.prepareUserOperation(accounts[0].address, mockIntents);
await keyring.prepareUserOperation(
accounts[0].address,
mockIntents,
keyringContext,
);

expect(mockSnapController.handleRequest).toHaveBeenCalledWith({
handler: 'onKeyringRequest',
Expand All @@ -1231,7 +1244,7 @@ describe('SnapKeyring', () => {
method: 'keyring_submitRequest',
params: {
id: expect.any(String),
scope: expect.any(String),
scope: toCaipChainId(CaipNamespaces.Eip155, keyringContext.chainId),
account: accounts[0].id,
request: {
method: 'eth_prepareUserOperation',
Expand All @@ -1249,7 +1262,11 @@ describe('SnapKeyring', () => {
});

await expect(
keyring.prepareUserOperation(accounts[0].address, mockIntents),
keyring.prepareUserOperation(
accounts[0].address,
mockIntents,
keyringContext,
),
).rejects.toThrow(regexForUUIDInRequiredSyncErrorMessage);
});
});
Expand Down Expand Up @@ -1279,7 +1296,11 @@ describe('SnapKeyring', () => {
result: mockExpectedPatch,
});

await keyring.patchUserOperation(accounts[0].address, mockUserOp);
await keyring.patchUserOperation(
accounts[0].address,
mockUserOp,
keyringContext

Check failure on line 1302 in src/SnapKeyring.test.ts

View workflow job for this annotation

GitHub Actions / Build, lint, and test / Lint (16.x)

Insert `,`

Check failure on line 1302 in src/SnapKeyring.test.ts

View workflow job for this annotation

GitHub Actions / Build, lint, and test / Lint (18.x)

Insert `,`

Check failure on line 1302 in src/SnapKeyring.test.ts

View workflow job for this annotation

GitHub Actions / Build, lint, and test / Lint (20.x)

Insert `,`
);

expect(mockSnapController.handleRequest).toHaveBeenCalledWith({
handler: 'onKeyringRequest',
Expand All @@ -1290,7 +1311,7 @@ describe('SnapKeyring', () => {
method: 'keyring_submitRequest',
params: {
id: expect.any(String),
scope: expect.any(String),
scope: toCaipChainId(CaipNamespaces.Eip155, keyringContext.chainId),
account: accounts[0].id,
request: {
method: 'eth_patchUserOperation',
Expand All @@ -1308,7 +1329,11 @@ describe('SnapKeyring', () => {
});

await expect(
keyring.patchUserOperation(accounts[0].address, mockUserOp),
keyring.patchUserOperation(
accounts[0].address,
mockUserOp,
keyringContext,
),
).rejects.toThrow(regexForUUIDInRequiredSyncErrorMessage);
});
});
Expand Down
22 changes: 22 additions & 0 deletions src/SnapKeyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
InternalAccount,
KeyringAccount,
KeyringResponse,
KeyringExecutionContext,
} from '@metamask/keyring-api';
import {
AccountCreatedEventStruct,
Expand Down Expand Up @@ -715,18 +716,27 @@ export class SnapKeyring extends EventEmitter {
*
* @param address - Address of the sender.
* @param transactions - Base transactions to include in the UserOperation.
* @param context - Keyring execution context.
* @returns A pseudo-UserOperation that can be used to construct a real.
*/
async prepareUserOperation(
address: string,
transactions: EthBaseTransaction[],
context: KeyringExecutionContext,
): Promise<EthBaseUserOperation> {
return strictMask(
await this.#submitRequest({
address,
method: EthMethod.PrepareUserOperation,
params: toJson<Json[]>(transactions),
expectSync: true,
// Assuming the chain ID is a number for now, later on the CAIP-2 chain ID
// would have to come from the caller directly, but for now this is good
// enough for our use case.
chainId: toCaipChainId(
CaipNamespaces.Eip155,
`${Number(context.chainId)}`,
),
}),
EthBaseUserOperationStruct,
);
Expand All @@ -738,18 +748,24 @@ export class SnapKeyring extends EventEmitter {
*
* @param address - Address of the sender.
* @param userOp - UserOperation to patch.
* @param context - Keyring execution context.
* @returns A patch to apply to the UserOperation.
*/
async patchUserOperation(
address: string,
userOp: EthUserOperation,
context: KeyringExecutionContext,
): Promise<EthUserOperationPatch> {
return strictMask(
await this.#submitRequest({
address,
method: EthMethod.PatchUserOperation,
params: toJson<Json[]>([userOp]),
expectSync: true,
chainId: toCaipChainId(
CaipNamespaces.Eip155,
`${Number(context.chainId)}`, // See `.prepareUserOperation` comment.
),
}),
EthUserOperationPatchStruct,
);
Expand All @@ -760,17 +776,23 @@ export class SnapKeyring extends EventEmitter {
*
* @param address - Address of the sender.
* @param userOp - UserOperation to sign.
* @param context - Leyring execution context.
* @returns The signature of the UserOperation.
*/
async signUserOperation(
address: string,
userOp: EthUserOperation,
context: KeyringExecutionContext,
): Promise<string> {
return strictMask(
await this.#submitRequest({
address,
method: EthMethod.SignUserOperation,
params: toJson<Json[]>([userOp]),
chainId: toCaipChainId(
CaipNamespaces.Eip155,
`${Number(context.chainId)}`, // See `.prepareUserOperation` comment.
),
}),
EthBytesStruct,
);
Expand Down

0 comments on commit 7f43cda

Please sign in to comment.