Remote Signer
SDK and Contract Interactions - DeeDive
Remote Signer Module
This module provides functionality to sign user operations using a remote signer. It includes the following key components:
Types
RemoteSignerParams: Defines the parameters required to create a remote signer.
export type RemoteSignerParams = {
account: LocalAccount,
chainId: number,
apiKey: string,
sessionKey: string,
permissionsBackendUrl?: string
}
Functions
signUserOp: Signs a user operation using the session key.
async function signUserOp(
account: Account,
chainId: number,
apiKey: string,
sessionKey: string,
userOp: UserOperation,
permissionsBackendUrl: string = PERMISSIONS_URL
): Promise<UserOperation> {
return signUserOpWithSessionKey(account.address, chainId, apiKey, sessionKey, userOp, permissionsBackendUrl);
}
toRemoteSigner: Converts a local account to an extended local account with remote signing capabilities.
export async function toRemoteSigner({
account,
chainId,
apiKey,
sessionKey
}: RemoteSignerParams): Promise<ExtendedLocalAccount> {
// Get sessionKey from apiKey and account
const sessionKeyResponse = await getSessionKey(account.address, chainId, apiKey, sessionKey);
// Create the extendedLocalAccount object and add the method
const extendedLocalAccount: ExtendedLocalAccount = {
...account, // Spread the properties of the original LocalAccount
async signUserOpWithRemoteSigner(userOp: UserOperation) {
const signedUserOp = await signUserOp(account, chainId, apiKey, sessionKeyResponse.sessionKey, userOp);
return signedUserOp;
},
async signMessage({ message }) {
throw new Error('signMessage with sessionKey not implemented');
},
async signTransaction(_, __) {
throw new Error('signTransaction with sessionKey not implemented');
},
async signTypedData<
const TTypedData extends TypedData | Record<string, unknown>,
TPrimaryType extends keyof TTypedData | 'EIP712Domain' = keyof TTypedData
>(typedData: TypedDataDefinition<TTypedData, TPrimaryType>) {
throw new Error('signTypedData not implemented');
},
};
return extendedLocalAccount;
}
signUserOperation: Signs a user operation using the remote signer.
export const signUserOperation = async (
etherspotWalletAccount: LocalAccount,
chainId: number,
apiKey: string,
sessionKey: string,
userOp: UserOperation
) => {
const remoteSigner: ExtendedLocalAccount = await toRemoteSigner({
account: etherspotWalletAccount,
chainId: chainId,
apiKey: apiKey,
sessionKey: sessionKey,
permissionsBackendUrl: PERMISSIONS_URL
});
const signedUserOp = await remoteSigner.signUserOpWithRemoteSigner(userOp);
if (!signedUserOp || !signedUserOp.signature || signedUserOp.signature === '0x') {
throw new Error('Failed to sign user operation');
}
return signedUserOp;
}
Pre-requisites for using a sessionKey in remote-signing
- EtherspotWallet account should have SessionKeyValidator module installed.
- SessionKeyValidator varies with the kind of operation performed, i.e there can be multiple kinds of SessionKeyValidator and the nonce to be used as part of userOp is generated from the address of
SessionKeyValidator
ERC20SessionKeyValidator is used to perform the erc20 based operations from etherspotWalletAddress
All validations on the UserOp (ERC20 operations) are done by ERC20SessionKeyValidator
module
Nonce used during the UserOp Estimation is to be from:
const erc20SessionKeyValidator = '';
BigNumber.from(erc20SessionKeyValidator)
This nonce is later used to identify the validatorModule used during the validationPhase in EntryPoint contract.
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
)
external
payable
virtual
override
onlyEntryPoint
payPrefund(missingAccountFunds)
returns (uint256 validSignature)
{
address validator;
// @notice validator encoding in nonce is just an example!
// @notice this is not part of the standard!
// Account Vendors may choose any other way to implement validator selection
uint256 nonce = userOp.nonce;
assembly {
validator := shr(96, nonce)
}