UNPKG

@zerodev/sdk

Version:

A utility library for working with ERC-4337

298 lines 12.8 kB
import { satisfies } from "semver"; import { concat, concatHex, maxUint16, maxUint192, pad, toHex, zeroAddress } from "viem"; import { getChainId } from "viem/actions"; import { encodeModuleInstallCallData as encodeModuleInstallCallDataEpV06 } from "../../accounts/kernel/utils/account/ep0_6/encodeModuleInstallCallData.js"; import { ONLY_ENTRYPOINT_HOOK_ADDRESS, VALIDATOR_MODE, VALIDATOR_TYPE } from "../../constants.js"; import { ValidatorMode } from "../../types/kernel.js"; import { getKernelV3Nonce } from "../kernel/utils/account/ep0_7/getKernelV3Nonce.js"; import { accountMetadata } from "../kernel/utils/common/accountMetadata.js"; import { getActionSelector } from "../kernel/utils/common/getActionSelector.js"; import { getEncodedPluginsData as getEncodedPluginsDataV1 } from "../kernel/utils/plugins/ep0_6/getEncodedPluginsData.js"; import { getPluginsEnableTypedData as getPluginsEnableTypedDataV1 } from "../kernel/utils/plugins/ep0_6/getPluginsEnableTypedData.js"; import { getEncodedPluginsData as getEncodedPluginsDataV2 } from "../kernel/utils/plugins/ep0_7/getEncodedPluginsData.js"; import { getPluginsEnableTypedData as getPluginsEnableTypedDataV2 } from "../kernel/utils/plugins/ep0_7/getPluginsEnableTypedData.js"; import { isPluginInitialized } from "../kernel/utils/plugins/ep0_7/isPluginInitialized.js"; export function isKernelPluginManager( // biome-ignore lint/suspicious/noExplicitAny: <explanation> plugin) { return plugin?.getPluginEnableSignature !== undefined; } export async function toKernelPluginManager(client, { sudo, regular, hook, pluginEnableSignature, validatorInitData, action, validAfter = 0, validUntil = 0, entryPoint, kernelVersion, chainId, isPreInstalled = false }) { if ((sudo && !satisfies(kernelVersion, sudo?.supportedKernelVersions)) || (regular && !satisfies(kernelVersion, regular?.supportedKernelVersions))) { throw new Error("Either sudo or/and regular validator version mismatch. Update to latest plugin package and use the proper plugin version"); } let pluginEnabled = isPreInstalled; const activeValidator = regular || sudo; if (!activeValidator) { throw new Error("One of `sudo` or `regular` validator must be set"); } action = { selector: action?.selector ?? getActionSelector(entryPoint.version), address: action?.address ?? zeroAddress }; if (entryPoint.version === "0.7" && (action.address.toLowerCase() !== zeroAddress.toLowerCase() || action.selector.toLowerCase() !== getActionSelector(entryPoint.version).toLowerCase()) && kernelVersion === "0.3.0") { action.hook = { address: action.hook?.address ?? ONLY_ENTRYPOINT_HOOK_ADDRESS }; } if (!action) { throw new Error("Action data must be set"); } const getSignatureData = async (accountAddress, selector, userOpSignature = "0x") => { if (!action) { throw new Error("Action data must be set"); } if (entryPoint.version === "0.6") { if (regular) { if (pluginEnabled) { return ValidatorMode.plugin; } if (await isPluginEnabled(accountAddress, selector)) { return ValidatorMode.plugin; } const enableSignature = await getPluginEnableSignature(accountAddress); if (!enableSignature) { throw new Error("Enable signature not set"); } return getEncodedPluginsDataV1({ accountAddress, enableSignature, action, validator: regular, validUntil, validAfter }); } else if (sudo) { return ValidatorMode.sudo; } else { throw new Error("One of `sudo` or `regular` validator must be set"); } } if (regular) { if (pluginEnabled) { return userOpSignature; } if (await isPluginEnabled(accountAddress, action.selector)) { return userOpSignature; } const enableSignature = await getPluginEnableSignature(accountAddress); return getEncodedPluginsDataV2({ enableSignature, userOpSignature, action, enableData: await regular.getEnableData(accountAddress), hook }); } else if (sudo) { return userOpSignature; } else { throw new Error("One of `sudo` or `regular` validator must be set"); } }; const isPluginEnabled = async (accountAddress, selector) => { if (isPreInstalled) return true; if (!action) { throw new Error("Action data must be set"); } if (!regular) throw new Error("regular validator not set"); if (entryPoint.version === "0.6") { return regular.isEnabled(accountAddress, selector); } const isEnabled = (await regular.isEnabled(accountAddress, action.selector)) || (await isPluginInitialized(client, accountAddress, regular.address)); if (isEnabled) { pluginEnabled = true; } return isEnabled; }; const getPluginEnableSignature = async (accountAddress) => { if (!action) { throw new Error("Action data must be set"); } if (pluginEnableSignature) return pluginEnableSignature; if (!sudo) throw new Error("sudo validator not set -- need it to enable the validator"); if (!regular) throw new Error("regular validator not set"); const { version } = await accountMetadata(client, accountAddress, kernelVersion); if (!chainId) { chainId = client.chain?.id ?? (await getChainId(client)); } let ownerSig; if (entryPoint.version === "0.6") { const typeData = await getPluginsEnableTypedDataV1({ accountAddress, chainId, kernelVersion: version ?? kernelVersion, action, validator: regular, validUntil, validAfter }); ownerSig = await sudo.signTypedData(typeData); pluginEnableSignature = ownerSig; return ownerSig; } const validatorNonce = await getKernelV3Nonce(client, accountAddress); const typedData = await getPluginsEnableTypedDataV2({ accountAddress, chainId, kernelVersion: version, action, hook, validator: regular, validatorNonce }); ownerSig = await sudo.signTypedData(typedData); pluginEnableSignature = ownerSig; return ownerSig; }; const getIdentifier = (isSudo = false) => { const validator = (isSudo ? sudo : regular) ?? activeValidator; return concat([ VALIDATOR_TYPE[validator.validatorType], validator.getIdentifier() ]); }; const getPluginsEnableTypedData = async (accountAddress) => { if (!action) { throw new Error("Action data must be set"); } if (!sudo) throw new Error("sudo validator not set -- need it to enable the validator"); if (!regular) throw new Error("regular validator not set"); const { version } = await accountMetadata(client, accountAddress, kernelVersion); const validatorNonce = await getKernelV3Nonce(client, accountAddress); if (!chainId) { chainId = client.chain?.id ?? (await getChainId(client)); } const typedData = await getPluginsEnableTypedDataV2({ accountAddress, chainId, kernelVersion: version, action, validator: regular, validatorNonce }); return typedData; }; return { sudoValidator: sudo, regularValidator: regular, activeValidatorMode: sudo && !regular ? "sudo" : "regular", ...activeValidator, hook, getIdentifier, encodeModuleInstallCallData: async (accountAddress) => { if (!action) { throw new Error("Action data must be set"); } if (!regular) throw new Error("regular validator not set"); if (entryPoint.version === "0.6") { return await encodeModuleInstallCallDataEpV06({ accountAddress, selector: action.selector, executor: action.address, validator: regular?.address, validUntil, validAfter, enableData: await regular.getEnableData(accountAddress) }); } throw new Error("EntryPoint v0.7 not supported yet"); }, signUserOperation: async (userOperation) => { const userOpSig = await activeValidator.signUserOperation(userOperation); if (entryPoint.version === "0.6") { return concatHex([ await getSignatureData(userOperation.sender, userOperation.callData.toString().slice(0, 10)), userOpSig ]); } return await getSignatureData(userOperation.sender, userOperation.callData.toString().slice(0, 10), userOpSig); }, getAction: () => { if (!action) { throw new Error("Action data must be set"); } return action; }, getValidityData: () => ({ validAfter, validUntil }), getStubSignature: async (userOperation) => { const userOpSig = await activeValidator.getStubSignature(userOperation); if (entryPoint.version === "0.6") { return concatHex([ await getSignatureData(userOperation.sender, userOperation.callData.toString().slice(0, 10)), userOpSig ]); } return await getSignatureData(userOperation.sender, userOperation.callData.toString().slice(0, 10), userOpSig); }, getNonceKey: async (accountAddress = zeroAddress, customNonceKey = 0n) => { if (!action) { throw new Error("Action data must be set"); } if (entryPoint.version === "0.6") { if (customNonceKey > maxUint192) { throw new Error("Custom nonce key must be equal or less than maxUint192 for 0.6"); } return await activeValidator.getNonceKey(accountAddress, customNonceKey); } if (customNonceKey > maxUint16) throw new Error("Custom nonce key must be equal or less than 2 bytes(maxUint16) for v0.7"); const validatorMode = !regular || (await isPluginEnabled(accountAddress, action.selector)) ? VALIDATOR_MODE.DEFAULT : VALIDATOR_MODE.ENABLE; const validatorType = regular ? VALIDATOR_TYPE[regular.validatorType] : VALIDATOR_TYPE.SUDO; const encoding = pad(concatHex([ validatorMode, // 1 byte validatorType, // 1 byte pad(activeValidator.getIdentifier(), { size: 20, dir: "right" }), // 20 bytes pad(toHex(await activeValidator.getNonceKey(accountAddress, customNonceKey)), { size: 2 }) // 2 byte ]), { size: 24 }); const encodedNonceKey = BigInt(encoding); return encodedNonceKey; }, getPluginEnableSignature, getPluginsEnableTypedData, getValidatorInitData: async () => { if (validatorInitData) return validatorInitData; return { validatorAddress: sudo?.address ?? activeValidator.address, enableData: (await sudo?.getEnableData()) ?? (await activeValidator.getEnableData()), identifier: pad(getIdentifier(true), { size: 21, dir: "right" }) }; }, signUserOperationWithActiveValidator: async (userOperation) => { return activeValidator.signUserOperation(userOperation); } }; } //# sourceMappingURL=toKernelPluginManager.js.map