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 bb4021b00..415e05b48 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 @@ -15,26 +15,26 @@ import { WalletGrantPermissionsReturnType, encodeAbiParameters, encodePacked, - keccak256, serializeSignature } from 'viem' import { publicKeyToAddress, sign } from 'viem/accounts' import { MOCK_VALIDATOR_ADDRESS, PERMISSION_VALIDATOR_ADDRESS, - SAFE7579_USER_OPERATION_BUILDER_ADDRESS, - WALLET_CONNECT_COSIGNER, - YESPOLICY + SAFE7579_USER_OPERATION_BUILDER_ADDRESS } from '@/utils/permissionValidatorUtils/constants' import { MultiKeySigner } from 'viem/_types/experimental/erc7715/types/signer' import { KEY_TYPES, bigIntReplacer, decodeDIDToPublicKey } from '@/utils/HelperUtil' import { isModuleInstalledAbi } from '@/utils/ERC7579AccountUtils' import { parsePublicKey as parsePasskeyPublicKey } from 'webauthn-p256' +import { enableSessionAbi } from '@/utils/permissionValidatorUtils/abi' import { - WebAuthnValidationDataAbi, - enableSessionAbi, - smartSessionAbi -} from '@/utils/permissionValidatorUtils/abi' + generateSignerId, + getDigest, + getEOAAndPasskeySignerInitData, + getPermissionContext, + perpareMockWCCosignerEnableSession +} from '@/utils/permissionValidatorUtils' export class SafeSmartAccountLib extends SmartAccountLib { protected ERC_7579_LAUNCHPAD_ADDRESS: Address = '0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE' @@ -124,11 +124,8 @@ export class SafeSmartAccountLib extends SmartAccountLib { if (!requestedSigner || requestedSigner.type !== 'keys') { throw new Error('Currently only supporting KeySigner and MultiKey Type for permissions') } - const signerId = this.generatePermissionSignerId(grantPermissionsRequestParams) - console.log({ signerId }) const typeSigner = requestedSigner as MultiKeySigner const publicKeys = typeSigner.data.ids.map(id => decodeDIDToPublicKey(id)) - // const [eoaPublicKey, passkeyPublicKey] = publicKeys let eoaPublicKey, passkeyPublicKey publicKeys.forEach(key => { if (key.keyType === KEY_TYPES.secp256k1) { @@ -141,67 +138,24 @@ export class SafeSmartAccountLib extends SmartAccountLib { if (!eoaPublicKey || !passkeyPublicKey) throw Error('Invalid EOA and passkey signers') const targetEOAAddress = publicKeyToAddress(eoaPublicKey as `0x${string}`) const parsedPasskeyPublicKey = parsePasskeyPublicKey(passkeyPublicKey as `0x${string}`) - const encodedSignersInitData = encodeAbiParameters( - [{ type: 'uint256' }, WebAuthnValidationDataAbi], - [ - BigInt(targetEOAAddress), - { - pubKeyX: parsedPasskeyPublicKey.x, - pubKeyY: parsedPasskeyPublicKey.y - } - ] - ) - - const userOpPolicies = [ - { - initData: '0x' as `0x${string}`, - policy: YESPOLICY - } - ] - const actionId = keccak256(signerId) // just a random id - const actions = [ - { - actionId: actionId, - actionPolicies: userOpPolicies - } - ] - - const enableSessionParams = { - isigner: WALLET_CONNECT_COSIGNER as `0x${string}`, - actions: actions, - isignerInitData: encodedSignersInitData, - userOpPolicies: userOpPolicies, - erc1271Policies: [], - permissionEnableSig: '0x' as `0x${string}` - } - const enableSessionHash = await this.publicClient.readContract({ - address: PERMISSION_VALIDATOR_ADDRESS, - abi: smartSessionAbi, - functionName: 'getDigest', - args: [signerId, this.client.account.address, enableSessionParams] + const encodedSignersInitData = getEOAAndPasskeySignerInitData(targetEOAAddress, { + pubKeyX: parsedPasskeyPublicKey.x, + pubKeyY: parsedPasskeyPublicKey.y + }) + const enableSession = perpareMockWCCosignerEnableSession(encodedSignersInitData) + const signerId = generateSignerId(grantPermissionsRequestParams) + const enableSessionHash = await getDigest(this.publicClient, { + signerId, + accountAddress: this.client.account.address, + enableSession: enableSession }) - - console.log({ digest: enableSessionHash }) const signature = await sign({ privateKey: this.getPrivateKey() as `0x${string}`, hash: enableSessionHash }) - const enableSessionScopeSignature: Hex = serializeSignature(signature) - - enableSessionParams.permissionEnableSig = encodePacked( - ['address', 'bytes'], - [MOCK_VALIDATOR_ADDRESS, enableSessionScopeSignature] // TODO: MOCK_VALIDATOR_ADDRESS? defaultValidator? - ) - const encodedEnableSessionData = encodeAbiParameters(enableSessionAbi, [enableSessionParams]) - console.log({ encodedEnableSessionData }) - // permissionContext = PermissionValidatorAddress [20bytes] + SignerId[bytes32] + EncodedEnableSessionData[bytes] - const permissionContext = encodePacked( - ['address', 'bytes1', 'bytes32', 'bytes'], - [PERMISSION_VALIDATOR_ADDRESS, '0x02', signerId, encodedEnableSessionData] - ) - + const enableSessionSignature: Hex = serializeSignature(signature) + const permissionContext = getPermissionContext(signerId, enableSession, enableSessionSignature) console.log({ permissionContext }) - console.log('Granting permissions...') return { @@ -215,22 +169,6 @@ export class SafeSmartAccountLib extends SmartAccountLib { } } - private generatePermissionSignerId( - grantPermissionsRequestParams: WalletGrantPermissionsParameters - ) { - const json = JSON.stringify(grantPermissionsRequestParams, (key, value) => { - // Remove undefined values - if (value === undefined) { - return null - } - return value - }) - const jsonBytes = new TextEncoder().encode(json) - const hash = keccak256(jsonBytes) - - return hash - } - private async ensureAccountDeployed(): Promise { if (!this.client?.account) { throw new Error('Client not initialized') diff --git a/advanced/wallets/react-wallet-v2/src/utils/permissionValidatorUtils/constants.ts b/advanced/wallets/react-wallet-v2/src/utils/permissionValidatorUtils/constants.ts index eb185dc8c..baa1a4155 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/permissionValidatorUtils/constants.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/permissionValidatorUtils/constants.ts @@ -10,4 +10,4 @@ export const VALIDATOR_ADDRESS: Address = '0x503b54Ed1E62365F0c9e4caF1479623b08a export const SECP256K1_SIGNATURE_VALIDATOR_ADDRESS: Address = '0x033f60A1035E64c96FD511abA61bA8d9276ADB4f' export const SAFE7579_USER_OPERATION_BUILDER_ADDRESS: Address = - '0xa6cdc0f868c5cd3dbf8cdb47a940fcb5de77e5e0' + '0xCd67aCD5d31969e2c368d6A1cfE1911932C744b1' diff --git a/advanced/wallets/react-wallet-v2/src/utils/permissionValidatorUtils/index.ts b/advanced/wallets/react-wallet-v2/src/utils/permissionValidatorUtils/index.ts index e9db68114..6f2569b20 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/permissionValidatorUtils/index.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/permissionValidatorUtils/index.ts @@ -1,4 +1,22 @@ -import { Address, Chain, Hex, encodePacked, keccak256 } from 'viem' +import { + Address, + Chain, + Hex, + PublicClient, + WalletGrantPermissionsParameters, + encodeAbiParameters, + encodePacked, + keccak256, + toBytes +} from 'viem' +import { GrantPermissionsParameters } from 'viem/experimental' +import { + MOCK_VALIDATOR_ADDRESS, + PERMISSION_VALIDATOR_ADDRESS, + WALLET_CONNECT_COSIGNER, + YESPOLICY +} from './constants' +import { enableSessionAbi, smartSessionAbi, WebAuthnValidationDataAbi } from './abi' export type SingleSignerPermission = { validUntil: number @@ -23,6 +41,128 @@ export type PermissionContext = { enableSig?: `0x${string}` } +export type ActionPolicy = { + initData: `0x${string}` + policy: `0x${string}` +} +export type UserOpPolicy = { + initData: `0x${string}` + policy: `0x${string}` +} +export type ERC1271Policy = { + initData: `0x${string}` + policy: `0x${string}` +} +export type ActionId = `0x${string}` + +export type Action = { + actionId: ActionId + actionPolicies: ActionPolicy[] +} + +export type EnableSession = { + isigner: `0x${string}` + isignerInitData: `0x${string}` + actions: Action[] + userOpPolicies: UserOpPolicy[] + erc1271Policies: ERC1271Policy[] + permissionEnableSig: `0x${string}` +} +export type PasskeyPublicKey = { + pubKeyX: bigint + pubKeyY: bigint +} + +export function perpareMockWCCosignerEnableSession(signersInitData: `0x${string}`): EnableSession { + const userOpPolicies: UserOpPolicy[] = [ + { + initData: '0x' as `0x${string}`, + policy: YESPOLICY + } + ] + const actionId = keccak256(toBytes('MockAction01')) // just a random id + const actions: Action[] = [ + { + actionId: actionId, + actionPolicies: userOpPolicies + } + ] + + const enableSessionParams: EnableSession = { + isigner: WALLET_CONNECT_COSIGNER as `0x${string}`, + actions: actions, + isignerInitData: signersInitData, + userOpPolicies: userOpPolicies, + erc1271Policies: [], + permissionEnableSig: '0x' as `0x${string}` + } + return enableSessionParams +} + +export function getEOAAndPasskeySignerInitData( + eoaAddress: Address, + { pubKeyX, pubKeyY }: PasskeyPublicKey +): `0x${string}` { + return encodeAbiParameters( + [{ type: 'uint256' }, WebAuthnValidationDataAbi], + [ + BigInt(eoaAddress), + { + pubKeyX, + pubKeyY + } + ] + ) +} + +export function generateSignerId(grantPermissionsRequestParams: WalletGrantPermissionsParameters) { + const json = JSON.stringify(grantPermissionsRequestParams, (key, value) => { + // Remove undefined values + if (value === undefined) { + return null + } + return value + }) + const jsonBytes = new TextEncoder().encode(json) + const hash = keccak256(jsonBytes) + + return hash +} + +export async function getDigest( + publicClient: PublicClient, + args: { + signerId: `0x${string}` + accountAddress: `0x${string}` + enableSession: EnableSession + } +): Promise<`0x${string}`> { + const { signerId, accountAddress, enableSession } = args + return await publicClient.readContract({ + address: PERMISSION_VALIDATOR_ADDRESS, + abi: smartSessionAbi, + functionName: 'getDigest', + args: [signerId, accountAddress, enableSession] + }) +} + +export function getPermissionContext( + signerId: `0x${string}`, + enableSession: EnableSession, + enableSessionSignature: `0x${string}` +) { + enableSession.permissionEnableSig = encodePacked( + ['address', 'bytes'], + [MOCK_VALIDATOR_ADDRESS, enableSessionSignature] // TODO: MOCK_VALIDATOR_ADDRESS? defaultValidator? + ) + const encodedEnableSessionData = encodeAbiParameters(enableSessionAbi, [enableSession]) + + return encodePacked( + ['address', 'bytes1', 'bytes32', 'bytes'], + [PERMISSION_VALIDATOR_ADDRESS, '0x02', signerId, encodedEnableSessionData] + ) +} + export function getPermissionId(permission: SingleSignerPermission): `0x${string}` { return keccak256( encodePacked(