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

chore: add Kernel smart account 7715 support #595

Merged
merged 4 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {
Address,
bytesToHex,
concat,
concatHex,
createPublicClient,
getAddress,
getTypesForEIP712Domain,
hashTypedData,
Hex,
hexToBytes,
http,
keccak256,
PrivateKeyAccount,
Expand All @@ -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'
Expand Down Expand Up @@ -44,14 +49,22 @@ 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,
SingleSignerPermission
} 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
Expand Down Expand Up @@ -279,6 +292,66 @@ export class KernelSmartAccountLib implements EIP155Wallet {
return serializedSessionKey
}

async grantPermissions(
grantPermissionsRequestParams: WalletGrantPermissionsParameters
): Promise<WalletGrantPermissionsReturnType> {
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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
import {
Address,
Hex,
WalletGrantPermissionsParameters,
WalletGrantPermissionsReturnType,
concatHex,
encodeFunctionData,
encodePacked,
Expand All @@ -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<SmartAccountClientConfig<EntryPoint>> {
Expand Down Expand Up @@ -105,8 +106,8 @@ export class SafeSmartAccountLib extends SmartAccountLib {
}

async grantPermissions(
grantPermissionsRequestParams: GrantPermissionsParameters
): Promise<GrantPermissionsReturnType> {
grantPermissionsRequestParams: WalletGrantPermissionsParameters
): Promise<WalletGrantPermissionsReturnType> {
if (!this.client?.account) {
throw new Error('Client not initialized')
}
Expand Down Expand Up @@ -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)
Expand All @@ -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[]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<SignClientTypes.EventArguments['session_request'], 'verifyContext'>

function getSmartWalletAddressFromSession(requestSession: SessionTypes.Struct) {
Expand Down Expand Up @@ -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<GrantPermissionsReturnType>(id, grantPermissionsResponse)
let grantPermissionsRequestParams: WalletGrantPermissionsParameters = request.params[0]
if (wallet instanceof SafeSmartAccountLib || wallet instanceof KernelSmartAccountLib) {
const grantPermissionsResponse: WalletGrantPermissionsReturnType =
await wallet.grantPermissions(grantPermissionsRequestParams)
return formatJsonRpcResult<WalletGrantPermissionsReturnType>(id, grantPermissionsResponse)
}

// for any other wallet instance return un_supported
Expand Down
2 changes: 1 addition & 1 deletion advanced/wallets/react-wallet-v2/src/utils/HelperUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"')
Expand Down
Loading