Skip to content

Commit 701062f

Browse files
chore: add Kernel smart account 7715 support (#595)
* chore: add Kernel smart account grant permissions request * fix: format * fix: use pubkey instead of address * fix: format
1 parent 6a94d64 commit 701062f

File tree

4 files changed

+90
-15
lines changed

4 files changed

+90
-15
lines changed

advanced/wallets/react-wallet-v2/src/lib/smart-accounts/KernelSmartAccountLib.ts

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import {
22
Address,
3+
bytesToHex,
34
concat,
45
concatHex,
56
createPublicClient,
7+
getAddress,
68
getTypesForEIP712Domain,
79
hashTypedData,
810
Hex,
11+
hexToBytes,
912
http,
1013
keccak256,
1114
PrivateKeyAccount,
@@ -14,9 +17,11 @@ import {
1417
Transport,
1518
TypedDataDefinition,
1619
validateTypedData,
20+
WalletGrantPermissionsParameters,
21+
WalletGrantPermissionsReturnType,
1722
zeroAddress
1823
} from 'viem'
19-
import { privateKeyToAccount, signMessage } from 'viem/accounts'
24+
import { privateKeyToAccount, publicKeyToAddress, signMessage } from 'viem/accounts'
2025
import { EIP155Wallet } from '../EIP155Lib'
2126
import { JsonRpcProvider } from '@ethersproject/providers'
2227
import { KernelValidator, signerToEcdsaValidator } from '@zerodev/ecdsa-validator'
@@ -44,14 +49,22 @@ import {
4449
SECP256K1_SIGNATURE_VALIDATOR_ADDRESS
4550
} from '@/utils/permissionValidatorUtils/constants'
4651
import { executeAbi } from '@/utils/safe7579AccountUtils/abis/Account'
47-
import { ENTRYPOINT_ADDRESS_V07_TYPE } from 'permissionless/_types/types'
4852
import {
4953
getPermissionScopeData,
5054
PermissionContext,
5155
SingleSignerPermission
5256
} from '@/utils/permissionValidatorUtils'
5357
import { KERNEL_V2_4, KERNEL_V3_1 } from '@zerodev/sdk/constants'
5458
import { KERNEL_V2_VERSION_TYPE, KERNEL_V3_VERSION_TYPE } from '@zerodev/sdk/types'
59+
import { decodeDIDToSecp256k1PublicKey } from '@/utils/HelperUtil'
60+
import { KeySigner } from 'viem/_types/experimental/erc7715/types/signer'
61+
62+
type DonutPurchasePermissionData = {
63+
target: string
64+
abi: any
65+
valueLimit: bigint
66+
functionName: string
67+
}
5568

5669
type SmartAccountLibOptions = {
5770
privateKey: string
@@ -279,6 +292,66 @@ export class KernelSmartAccountLib implements EIP155Wallet {
279292
return serializedSessionKey
280293
}
281294

295+
async grantPermissions(
296+
grantPermissionsRequestParams: WalletGrantPermissionsParameters
297+
): Promise<WalletGrantPermissionsReturnType> {
298+
if (!this.publicClient) {
299+
throw new Error('Client not initialized')
300+
}
301+
console.log('grantPermissions', { grantPermissionsRequestParams })
302+
303+
const signer = grantPermissionsRequestParams.signer
304+
// check if signer type is AccountSigner then it will have data.id
305+
if (signer && !(signer.type === 'key')) {
306+
throw Error('Currently only supporting KeySigner Type for permissions')
307+
}
308+
309+
const typedSigner = signer as KeySigner
310+
const pubkey = decodeDIDToSecp256k1PublicKey(typedSigner.data.id)
311+
312+
const emptySessionKeySigner = addressToEmptyAccount(publicKeyToAddress(pubkey as `0x${string}`))
313+
314+
const permissions = grantPermissionsRequestParams.permissions
315+
const zeroDevPermissions = []
316+
317+
for (const permission of permissions) {
318+
if (permission.type === 'donut-purchase') {
319+
const data = permission.data as DonutPurchasePermissionData
320+
zeroDevPermissions.push({
321+
target: data.target,
322+
abi: data.abi,
323+
valueLimit: data.valueLimit,
324+
functionName: data.functionName
325+
})
326+
}
327+
}
328+
const sessionKeyValidator = await signerToSessionKeyValidator(this.publicClient, {
329+
signer: emptySessionKeySigner,
330+
validatorData: {
331+
// @ts-ignore
332+
permissions: zeroDevPermissions
333+
},
334+
kernelVersion: this.kernelVersion,
335+
entryPoint: this.entryPoint
336+
})
337+
const sessionKeyAccount = await createKernelAccount(this.publicClient, {
338+
plugins: {
339+
sudo: this.validator,
340+
regular: sessionKeyValidator
341+
},
342+
entryPoint: this.entryPoint,
343+
kernelVersion: this.kernelVersion
344+
})
345+
346+
const serializedSessionKey = await serializeSessionKeyAccount(sessionKeyAccount)
347+
348+
return {
349+
permissionsContext: serializedSessionKey,
350+
grantedPermissions: grantPermissionsRequestParams.permissions,
351+
expiry: grantPermissionsRequestParams.expiry
352+
} as WalletGrantPermissionsReturnType
353+
}
354+
282355
async updateCoSigners(signers: `0x${string}`[]) {
283356
if (!this.client || !this.publicClient || !this.client.account) {
284357
throw new Error('Client not initialized')

advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
import {
1515
Address,
1616
Hex,
17+
WalletGrantPermissionsParameters,
18+
WalletGrantPermissionsReturnType,
1719
concatHex,
1820
encodeFunctionData,
1921
encodePacked,
@@ -31,9 +33,8 @@ import { Execution } from '@/utils/safe7579AccountUtils/userop'
3133
import { isModuleInstalledAbi } from '@/utils/safe7579AccountUtils/abis/Account'
3234
import { ethers } from 'ethers'
3335
import { SAFE7579_USER_OPERATION_BUILDER_ADDRESS } from '@/utils/safe7579AccountUtils/constants'
34-
import { GrantPermissionsParameters, GrantPermissionsReturnType } from 'viem/experimental'
3536
import { KeySigner } from 'viem/_types/experimental/erc7715/types/signer'
36-
import { decodeDIDToSECP256k1PublicKey } from '@/utils/HelperUtil'
37+
import { decodeDIDToSecp256k1PublicKey } from '@/utils/HelperUtil'
3738

3839
export class SafeSmartAccountLib extends SmartAccountLib {
3940
async getClientConfig(): Promise<SmartAccountClientConfig<EntryPoint>> {
@@ -105,8 +106,8 @@ export class SafeSmartAccountLib extends SmartAccountLib {
105106
}
106107

107108
async grantPermissions(
108-
grantPermissionsRequestParams: GrantPermissionsParameters
109-
): Promise<GrantPermissionsReturnType> {
109+
grantPermissionsRequestParams: WalletGrantPermissionsParameters
110+
): Promise<WalletGrantPermissionsReturnType> {
110111
if (!this.client?.account) {
111112
throw new Error('Client not initialized')
112113
}
@@ -138,7 +139,7 @@ export class SafeSmartAccountLib extends SmartAccountLib {
138139
}
139140
const typedSigner = signer as KeySigner
140141
const id = typedSigner.data.id
141-
const publicKey = decodeDIDToSECP256k1PublicKey(id)
142+
const publicKey = decodeDIDToSecp256k1PublicKey(id)
142143
const targetAddress = publicKeyToAddress(publicKey as `0x${string}`)
143144
console.log({ targetAddress })
144145
const { permissionsContext } = await this.getAllowedPermissionsAndData(targetAddress)
@@ -153,7 +154,7 @@ export class SafeSmartAccountLib extends SmartAccountLib {
153154
userOpBuilder: SAFE7579_USER_OPERATION_BUILDER_ADDRESS,
154155
submitToAddress: this.client.account.address
155156
}
156-
} as GrantPermissionsReturnType
157+
} as WalletGrantPermissionsReturnType
157158
}
158159

159160
private async setupSafe7579(calls: Execution | Execution[]) {

advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { SafeSmartAccountLib } from '@/lib/smart-accounts/SafeSmartAccountLib'
77
import { web3wallet } from './WalletConnectUtil'
88
import { smartAccountWallets } from './SmartAccountUtil'
99
import { GrantPermissionsParameters, GrantPermissionsReturnType } from 'viem/experimental'
10+
import { KernelSmartAccountLib } from '@/lib/smart-accounts/KernelSmartAccountLib'
11+
import { WalletGrantPermissionsParameters, WalletGrantPermissionsReturnType } from 'viem'
1012
type RequestEventArgs = Omit<SignClientTypes.EventArguments['session_request'], 'verifyContext'>
1113

1214
function getSmartWalletAddressFromSession(requestSession: SessionTypes.Struct) {
@@ -43,12 +45,11 @@ export async function approveEIP7715Request(requestEvent: RequestEventArgs) {
4345
switch (request.method) {
4446
case EIP7715_METHOD.WALLET_GRANT_PERMISSIONS: {
4547
const wallet = getSmartWalletAddressFromSession(requestSession)
46-
let grantPermissionsRequestParams: GrantPermissionsParameters = request.params[0]
47-
if (wallet instanceof SafeSmartAccountLib) {
48-
const grantPermissionsResponse: GrantPermissionsReturnType = await wallet.grantPermissions(
49-
grantPermissionsRequestParams
50-
)
51-
return formatJsonRpcResult<GrantPermissionsReturnType>(id, grantPermissionsResponse)
48+
let grantPermissionsRequestParams: WalletGrantPermissionsParameters = request.params[0]
49+
if (wallet instanceof SafeSmartAccountLib || wallet instanceof KernelSmartAccountLib) {
50+
const grantPermissionsResponse: WalletGrantPermissionsReturnType =
51+
await wallet.grantPermissions(grantPermissionsRequestParams)
52+
return formatJsonRpcResult<WalletGrantPermissionsReturnType>(id, grantPermissionsResponse)
5253
}
5354

5455
// for any other wallet instance return un_supported

advanced/wallets/react-wallet-v2/src/utils/HelperUtil.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export function styledToast(message: string, type: string) {
186186
}
187187
}
188188

189-
export const decodeDIDToSECP256k1PublicKey = (did: string): string => {
189+
export const decodeDIDToSecp256k1PublicKey = (did: string): string => {
190190
// Check if the DID starts with the correct prefix
191191
if (!did.startsWith('did:key:zQ3s')) {
192192
throw new Error('Invalid DID format. Must start with "did:key:zQ3s"')

0 commit comments

Comments
 (0)