diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/KernelSmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/KernelSmartAccountLib.ts index b1dd54bd4..883caa8ca 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/KernelSmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/KernelSmartAccountLib.ts @@ -1,11 +1,14 @@ import { Address, + bytesToHex, concat, concatHex, createPublicClient, + getAddress, getTypesForEIP712Domain, hashTypedData, Hex, + hexToBytes, http, keccak256, PrivateKeyAccount, @@ -14,9 +17,11 @@ import { Transport, TypedDataDefinition, validateTypedData, + WalletGrantPermissionsParameters, + WalletGrantPermissionsReturnType, zeroAddress } from 'viem' -import { privateKeyToAccount, signMessage } from 'viem/accounts' +import { privateKeyToAccount, publicKeyToAddress, signMessage } from 'viem/accounts' import { EIP155Wallet } from '../EIP155Lib' import { JsonRpcProvider } from '@ethersproject/providers' import { KernelValidator, signerToEcdsaValidator } from '@zerodev/ecdsa-validator' @@ -44,7 +49,6 @@ import { SECP256K1_SIGNATURE_VALIDATOR_ADDRESS } from '@/utils/permissionValidatorUtils/constants' import { executeAbi } from '@/utils/safe7579AccountUtils/abis/Account' -import { ENTRYPOINT_ADDRESS_V07_TYPE } from 'permissionless/_types/types' import { getPermissionScopeData, PermissionContext, @@ -52,6 +56,15 @@ import { } from '@/utils/permissionValidatorUtils' import { KERNEL_V2_4, KERNEL_V3_1 } from '@zerodev/sdk/constants' import { KERNEL_V2_VERSION_TYPE, KERNEL_V3_VERSION_TYPE } from '@zerodev/sdk/types' +import { decodeDIDToSecp256k1PublicKey } from '@/utils/HelperUtil' +import { KeySigner } from 'viem/_types/experimental/erc7715/types/signer' + +type DonutPurchasePermissionData = { + target: string + abi: any + valueLimit: bigint + functionName: string +} type SmartAccountLibOptions = { privateKey: string @@ -279,6 +292,66 @@ export class KernelSmartAccountLib implements EIP155Wallet { return serializedSessionKey } + async grantPermissions( + grantPermissionsRequestParams: WalletGrantPermissionsParameters + ): Promise { + if (!this.publicClient) { + throw new Error('Client not initialized') + } + console.log('grantPermissions', { grantPermissionsRequestParams }) + + const signer = grantPermissionsRequestParams.signer + // check if signer type is AccountSigner then it will have data.id + if (signer && !(signer.type === 'key')) { + throw Error('Currently only supporting KeySigner Type for permissions') + } + + const typedSigner = signer as KeySigner + const pubkey = decodeDIDToSecp256k1PublicKey(typedSigner.data.id) + + const emptySessionKeySigner = addressToEmptyAccount(publicKeyToAddress(pubkey as `0x${string}`)) + + const permissions = grantPermissionsRequestParams.permissions + const zeroDevPermissions = [] + + for (const permission of permissions) { + if (permission.type === 'donut-purchase') { + const data = permission.data as DonutPurchasePermissionData + zeroDevPermissions.push({ + target: data.target, + abi: data.abi, + valueLimit: data.valueLimit, + functionName: data.functionName + }) + } + } + const sessionKeyValidator = await signerToSessionKeyValidator(this.publicClient, { + signer: emptySessionKeySigner, + validatorData: { + // @ts-ignore + permissions: zeroDevPermissions + }, + kernelVersion: this.kernelVersion, + entryPoint: this.entryPoint + }) + const sessionKeyAccount = await createKernelAccount(this.publicClient, { + plugins: { + sudo: this.validator, + regular: sessionKeyValidator + }, + entryPoint: this.entryPoint, + kernelVersion: this.kernelVersion + }) + + const serializedSessionKey = await serializeSessionKeyAccount(sessionKeyAccount) + + return { + permissionsContext: serializedSessionKey, + grantedPermissions: grantPermissionsRequestParams.permissions, + expiry: grantPermissionsRequestParams.expiry + } as WalletGrantPermissionsReturnType + } + async updateCoSigners(signers: `0x${string}`[]) { if (!this.client || !this.publicClient || !this.client.account) { throw new Error('Client not initialized') diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts index 502545eb7..9980c3b53 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts @@ -14,6 +14,8 @@ import { import { Address, Hex, + WalletGrantPermissionsParameters, + WalletGrantPermissionsReturnType, concatHex, encodeFunctionData, encodePacked, @@ -31,9 +33,8 @@ import { Execution } from '@/utils/safe7579AccountUtils/userop' import { isModuleInstalledAbi } from '@/utils/safe7579AccountUtils/abis/Account' import { ethers } from 'ethers' import { SAFE7579_USER_OPERATION_BUILDER_ADDRESS } from '@/utils/safe7579AccountUtils/constants' -import { GrantPermissionsParameters, GrantPermissionsReturnType } from 'viem/experimental' import { KeySigner } from 'viem/_types/experimental/erc7715/types/signer' -import { decodeDIDToSECP256k1PublicKey } from '@/utils/HelperUtil' +import { decodeDIDToSecp256k1PublicKey } from '@/utils/HelperUtil' export class SafeSmartAccountLib extends SmartAccountLib { async getClientConfig(): Promise> { @@ -105,8 +106,8 @@ export class SafeSmartAccountLib extends SmartAccountLib { } async grantPermissions( - grantPermissionsRequestParams: GrantPermissionsParameters - ): Promise { + grantPermissionsRequestParams: WalletGrantPermissionsParameters + ): Promise { if (!this.client?.account) { throw new Error('Client not initialized') } @@ -138,7 +139,7 @@ export class SafeSmartAccountLib extends SmartAccountLib { } const typedSigner = signer as KeySigner const id = typedSigner.data.id - const publicKey = decodeDIDToSECP256k1PublicKey(id) + const publicKey = decodeDIDToSecp256k1PublicKey(id) const targetAddress = publicKeyToAddress(publicKey as `0x${string}`) console.log({ targetAddress }) const { permissionsContext } = await this.getAllowedPermissionsAndData(targetAddress) @@ -153,7 +154,7 @@ export class SafeSmartAccountLib extends SmartAccountLib { userOpBuilder: SAFE7579_USER_OPERATION_BUILDER_ADDRESS, submitToAddress: this.client.account.address } - } as GrantPermissionsReturnType + } as WalletGrantPermissionsReturnType } private async setupSafe7579(calls: Execution | Execution[]) { diff --git a/advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts b/advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts index 45587428a..8b1c71f8f 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts @@ -7,6 +7,8 @@ import { SafeSmartAccountLib } from '@/lib/smart-accounts/SafeSmartAccountLib' import { web3wallet } from './WalletConnectUtil' import { smartAccountWallets } from './SmartAccountUtil' import { GrantPermissionsParameters, GrantPermissionsReturnType } from 'viem/experimental' +import { KernelSmartAccountLib } from '@/lib/smart-accounts/KernelSmartAccountLib' +import { WalletGrantPermissionsParameters, WalletGrantPermissionsReturnType } from 'viem' type RequestEventArgs = Omit function getSmartWalletAddressFromSession(requestSession: SessionTypes.Struct) { @@ -43,12 +45,11 @@ export async function approveEIP7715Request(requestEvent: RequestEventArgs) { switch (request.method) { case EIP7715_METHOD.WALLET_GRANT_PERMISSIONS: { const wallet = getSmartWalletAddressFromSession(requestSession) - let grantPermissionsRequestParams: GrantPermissionsParameters = request.params[0] - if (wallet instanceof SafeSmartAccountLib) { - const grantPermissionsResponse: GrantPermissionsReturnType = await wallet.grantPermissions( - grantPermissionsRequestParams - ) - return formatJsonRpcResult(id, grantPermissionsResponse) + let grantPermissionsRequestParams: WalletGrantPermissionsParameters = request.params[0] + if (wallet instanceof SafeSmartAccountLib || wallet instanceof KernelSmartAccountLib) { + const grantPermissionsResponse: WalletGrantPermissionsReturnType = + await wallet.grantPermissions(grantPermissionsRequestParams) + return formatJsonRpcResult(id, grantPermissionsResponse) } // for any other wallet instance return un_supported diff --git a/advanced/wallets/react-wallet-v2/src/utils/HelperUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/HelperUtil.ts index 73b8ea235..380178d64 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/HelperUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/HelperUtil.ts @@ -186,7 +186,7 @@ export function styledToast(message: string, type: string) { } } -export const decodeDIDToSECP256k1PublicKey = (did: string): string => { +export const decodeDIDToSecp256k1PublicKey = (did: string): string => { // Check if the DID starts with the correct prefix if (!did.startsWith('did:key:zQ3s')) { throw new Error('Invalid DID format. Must start with "did:key:zQ3s"')