Skip to content

Commit 644d96d

Browse files
committed
Add signDelegation method and action
Add requireApproval option.
1 parent dc7db83 commit 644d96d

File tree

2 files changed

+112
-9
lines changed

2 files changed

+112
-9
lines changed

packages/signature-controller/src/SignatureController.ts

+98-9
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
type AddLog,
3030
} from '@metamask/logging-controller';
3131
import type { NetworkControllerGetNetworkClientByIdAction } from '@metamask/network-controller';
32-
import type { Hex, Json } from '@metamask/utils';
32+
import { hexToNumber, type Hex, type Json } from '@metamask/utils';
3333
// This package purposefully relies on Node's EventEmitter module.
3434
// eslint-disable-next-line import-x/no-nodejs-modules
3535
import EventEmitter from 'events';
@@ -46,6 +46,8 @@ import type {
4646
TypedSigningOptions,
4747
LegacyStateMessage,
4848
StateSIWEMessage,
49+
TypedData,
50+
Delegation,
4951
} from './types';
5052
import { DECODING_API_ERRORS, decodeSignature } from './utils/decoding-api';
5153
import {
@@ -131,12 +133,19 @@ export type GetSignatureState = ControllerGetStateAction<
131133
SignatureControllerState
132134
>;
133135

136+
export type SignDelegationAction = {
137+
type: `SignatureController:signDelegation`;
138+
handler: SignatureController['signDelegation'];
139+
};
140+
134141
export type SignatureStateChange = ControllerStateChangeEvent<
135142
typeof controllerName,
136143
SignatureControllerState
137144
>;
138145

139-
export type SignatureControllerActions = GetSignatureState;
146+
export type SignatureControllerActions =
147+
| GetSignatureState
148+
| SignDelegationAction;
140149

141150
export type SignatureControllerEvents = SignatureStateChange;
142151

@@ -232,6 +241,11 @@ export class SignatureController extends BaseController<
232241
this.#trace = trace ?? (((_request, fn) => fn?.()) as TraceCallback);
233242
this.#decodingApiUrl = decodingApiUrl;
234243
this.#isDecodeSignatureRequestEnabled = isDecodeSignatureRequestEnabled;
244+
245+
this.messagingSystem.registerActionHandler(
246+
`${this.name}:signDelegation`,
247+
this.signDelegation.bind(this),
248+
);
235249
}
236250

237251
/**
@@ -344,6 +358,7 @@ export class SignatureController extends BaseController<
344358
* @param version - The version of the signTypedData request.
345359
* @param signingOptions - Options for signing the typed message.
346360
* @param options - An options bag for the method.
361+
* @param options.requireApproval - Whether to require user approval for the signature.
347362
* @param options.traceContext - The parent context for any new traces.
348363
* @returns Promise resolving to the raw signature hash generated from the signature request.
349364
*/
@@ -352,7 +367,7 @@ export class SignatureController extends BaseController<
352367
request: OriginalRequest,
353368
version: string,
354369
signingOptions?: TypedSigningOptions,
355-
options: { traceContext?: TraceContext } = {},
370+
options: { requireApproval?: boolean; traceContext?: TraceContext } = {},
356371
): Promise<string> {
357372
const chainId = this.#getChainId(request);
358373
const internalAccounts = this.#getInternalAccounts();
@@ -374,6 +389,7 @@ export class SignatureController extends BaseController<
374389
approvalType: ApprovalType.EthSignTypedData,
375390
messageParams: normalizedMessageParams,
376391
request,
392+
requireApproval: options.requireApproval,
377393
signingOptions,
378394
traceContext: options.traceContext,
379395
type: SignatureRequestType.TypedSign,
@@ -440,6 +456,75 @@ export class SignatureController extends BaseController<
440456
this.setTypedMessageInProgress(signatureRequestId);
441457
}
442458

459+
async signDelegation({
460+
chainId,
461+
delegation,
462+
delegationManagerAddress,
463+
from,
464+
networkClientId,
465+
origin,
466+
requireApproval,
467+
}: {
468+
chainId: Hex;
469+
delegation: Delegation;
470+
delegationManagerAddress: Hex;
471+
from: Hex;
472+
networkClientId: string;
473+
origin?: string;
474+
requireApproval?: boolean;
475+
}) {
476+
const EIP712Domain = [
477+
{ name: 'name', type: 'string' },
478+
{ name: 'version', type: 'string' },
479+
{ name: 'chainId', type: 'uint256' },
480+
{ name: 'verifyingContract', type: 'address' },
481+
];
482+
483+
const SIGNABLE_DELEGATION_TYPED_DATA = {
484+
EIP712Domain,
485+
Caveat: [
486+
{ name: 'enforcer', type: 'address' },
487+
{ name: 'terms', type: 'bytes' },
488+
],
489+
Delegation: [
490+
{ name: 'delegate', type: 'address' },
491+
{ name: 'delegator', type: 'address' },
492+
{ name: 'authority', type: 'bytes32' },
493+
{ name: 'caveats', type: 'Caveat[]' },
494+
{ name: 'salt', type: 'uint256' },
495+
],
496+
};
497+
498+
const data: TypedData = {
499+
types: SIGNABLE_DELEGATION_TYPED_DATA,
500+
primaryType: 'Delegation',
501+
domain: {
502+
chainId: String(hexToNumber(chainId)),
503+
name: 'DelegationManager',
504+
version: '1',
505+
verifyingContract: delegationManagerAddress,
506+
},
507+
message: { ...delegation, chainId: hexToNumber(chainId) },
508+
};
509+
510+
return await this.newUnsignedTypedMessage(
511+
{
512+
data,
513+
from,
514+
origin,
515+
version: SignTypedDataVersion.V4,
516+
},
517+
{
518+
networkClientId,
519+
origin,
520+
params: [],
521+
},
522+
SignTypedDataVersion.V4,
523+
undefined,
524+
{ requireApproval },
525+
);
526+
}
527+
443528
#parseTypedData(
444529
messageParams: MessageParamsTyped,
445530
version?: SignTypedDataVersion,
@@ -466,6 +551,7 @@ export class SignatureController extends BaseController<
466551
type,
467552
approvalType,
468553
version,
554+
requireApproval,
469555
signingOptions,
470556
traceContext,
471557
}: {
@@ -475,6 +561,7 @@ export class SignatureController extends BaseController<
475561
type: SignatureRequestType;
476562
approvalType: ApprovalType;
477563
version?: SignTypedDataVersion;
564+
requireApproval?: boolean;
478565
signingOptions?: TypedSigningOptions;
479566
traceContext?: TraceContext;
480567
}): Promise<string> {
@@ -505,12 +592,14 @@ export class SignatureController extends BaseController<
505592
this.#decodePermitSignatureRequest(metadata.id, request, chainId);
506593

507594
try {
508-
resultCallbacks = await this.#processApproval({
509-
approvalType,
510-
metadata,
511-
request,
512-
traceContext,
513-
});
595+
if (requireApproval !== false) {
596+
resultCallbacks = await this.#processApproval({
597+
approvalType,
598+
metadata,
599+
request,
600+
traceContext,
601+
});
602+
}
514603

515604
await this.#approveAndSignRequest(metadata, traceContext);
516605
} catch (error) {

packages/signature-controller/src/types.ts

+14
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,17 @@ export enum SignatureRequestType {
205205
PersonalSign = 'personal_sign',
206206
TypedSign = 'eth_signTypedData',
207207
}
208+
209+
export type Caveat = {
210+
enforcer: Hex;
211+
terms: Hex;
212+
args: Hex;
213+
};
214+
215+
export type Delegation = {
216+
delegate: Hex;
217+
delegator: Hex;
218+
authority: Hex;
219+
caveats: Caveat[];
220+
salt: number;
221+
};

0 commit comments

Comments
 (0)