diff --git a/.size-limit.json b/.size-limit.json index baa5267..1b3196d 100644 --- a/.size-limit.json +++ b/.size-limit.json @@ -13,7 +13,7 @@ { "name": "module-sdk (minimal surface - tree-shaking)", "path": "./src/_esm/index.js", - "limit": "14 kB", + "limit": "15 kB", "import": "{ installModule }" } ] diff --git a/src/account/api/encodeModuleInstallationData.ts b/src/account/api/encodeModuleInstallationData.ts new file mode 100644 index 0000000..d687185 --- /dev/null +++ b/src/account/api/encodeModuleInstallationData.ts @@ -0,0 +1,18 @@ +import { Hex } from 'viem' +import { Account } from '../types' +import { getAccountImplementation } from './getAccountImplementation' +import { Module } from '../../module/types' + +export const encodeModuleInstallationData = ({ + account, + module, +}: { + account: Account + module: Module +}): Hex => { + const accountImplementation = getAccountImplementation({ account }) + return accountImplementation.encodeModuleInstallationData({ + account, + module, + }) +} diff --git a/src/account/api/encodeModuleUninstallationData.ts b/src/account/api/encodeModuleUninstallationData.ts new file mode 100644 index 0000000..ce6d1f1 --- /dev/null +++ b/src/account/api/encodeModuleUninstallationData.ts @@ -0,0 +1,21 @@ +import { Hex, PublicClient } from 'viem' +import { Account } from '../types' +import { getAccountImplementation } from './getAccountImplementation' +import { Module } from '../../module/types' + +export const encodeModuleUninstallationData = async ({ + client, + account, + module, +}: { + client: PublicClient + account: Account + module: Module +}): Promise => { + const accountImplementation = getAccountImplementation({ account }) + return await accountImplementation.encodeModuleUninstallationData({ + client, + account, + module, + }) +} diff --git a/src/account/erc7579-implementation/api/encodeModuleInstallationData.ts b/src/account/erc7579-implementation/api/encodeModuleInstallationData.ts new file mode 100644 index 0000000..48466ae --- /dev/null +++ b/src/account/erc7579-implementation/api/encodeModuleInstallationData.ts @@ -0,0 +1,29 @@ +import { Account } from '../../types' +import { Hex, encodePacked } from 'viem' +import { Module, CallType } from '../../../module/types' + +export const encodeModuleInstallationData = ({ + account, + module, +}: { + account: Account + module: Module +}): Hex => { + switch (module.type) { + case 'validator': + case 'executor': + case 'hook': + return module.initData + case 'fallback': + return encodePacked( + ['bytes4', 'bytes1', 'bytes'], + [ + module.selector!, + module.callType ?? CallType.CALLTYPE_SINGLE, + module.initData, + ], + ) + default: + throw new Error(`Unknown module type ${module.type}`) + } +} diff --git a/src/account/erc7579-implementation/api/encodeModuleUninstallationData.ts b/src/account/erc7579-implementation/api/encodeModuleUninstallationData.ts new file mode 100644 index 0000000..8ca1e4f --- /dev/null +++ b/src/account/erc7579-implementation/api/encodeModuleUninstallationData.ts @@ -0,0 +1,37 @@ +import { Account } from '../../types' +import { Hex, PublicClient, encodePacked, encodeAbiParameters } from 'viem' +import { Module, CallType } from '../../../module/types' +import { getPreviousModule } from '../../../common' + +export const encodeModuleUninstallationData = async ({ + client, + account, + module, +}: { + client: PublicClient + account: Account + module: Module +}): Promise => { + switch (module.type) { + case 'validator': + case 'executor': + const prev = await getPreviousModule({ client, account, module }) + return encodeAbiParameters( + [ + { name: 'prev', type: 'address' }, + { name: 'disableModuleData', type: 'bytes' }, + ], + [prev, module.deInitData], + ) + + case 'hook': + return module.deInitData + case 'fallback': + return encodePacked( + ['bytes4', 'bytes'], + [module.selector!, module.deInitData], + ) + default: + throw new Error(`Unknown module type ${module.type}`) + } +} diff --git a/src/account/erc7579-implementation/api/uninstallModule.ts b/src/account/erc7579-implementation/api/uninstallModule.ts index 9454a6e..8920674 100644 --- a/src/account/erc7579-implementation/api/uninstallModule.ts +++ b/src/account/erc7579-implementation/api/uninstallModule.ts @@ -50,7 +50,7 @@ const _uninstallModule = async ({ const isInstalled = await isModuleInstalled({ client, account, module }) if (isInstalled) { - let moduleData = module.initData || '0x' + let moduleData = module.deInitData || '0x' if (module.type === 'validator' || module.type === 'executor') { const prev = await getPreviousModule({ client, account, module }) moduleData = encodeAbiParameters( @@ -111,7 +111,7 @@ const _uninstallFallback = async ({ module.module, encodePacked( ['bytes4', 'bytes'], - [module.selector!, module.initData ?? '0x'], + [module.selector!, module.deInitData], ), ], }) diff --git a/src/account/erc7579-implementation/index.ts b/src/account/erc7579-implementation/index.ts index 4f7f6e7..49da02a 100644 --- a/src/account/erc7579-implementation/index.ts +++ b/src/account/erc7579-implementation/index.ts @@ -12,6 +12,8 @@ import { isModuleInstalled } from './api/isModuleInstalled' import { uninstallModule } from './api/uninstallModule' import { encode1271Signature } from './api/encode1271Signature' import { encode1271Hash } from './api/encode1271Hash' +import { encodeModuleInstallationData } from './api/encodeModuleInstallationData' +import { encodeModuleUninstallationData } from './api/encodeModuleUninstallationData' export class ERC7579Implementation { getInstalledModules = async ({ @@ -77,4 +79,26 @@ export class ERC7579Implementation { }: ERC1271HashParams): Hex => { return encode1271Hash({ account, chainId, validator, hash }) } + + encodeModuleInstallationData = ({ + account, + module, + }: { + account: Account + module: Module + }): Hex => { + return encodeModuleInstallationData({ account, module }) + } + + encodeModuleUninstallationData = async ({ + client, + account, + module, + }: { + client: PublicClient + account: Account + module: Module + }): Promise => { + return await encodeModuleUninstallationData({ client, account, module }) + } } diff --git a/src/account/kernel/api/encodeModuleInstallationData.ts b/src/account/kernel/api/encodeModuleInstallationData.ts new file mode 100644 index 0000000..a6319c2 --- /dev/null +++ b/src/account/kernel/api/encodeModuleInstallationData.ts @@ -0,0 +1,48 @@ +import { Account } from '../../types' +import { Hex, encodePacked, encodeAbiParameters } from 'viem' +import { KernelModule } from '../types' + +export const encodeModuleInstallationData = ({ + account, + module, +}: { + account: Account + module: KernelModule +}): Hex => { + switch (module.type) { + case 'validator': + case 'executor': + return encodePacked( + ['address', 'bytes'], + [ + module.hook!, + encodeAbiParameters( + [{ type: 'bytes' }, { type: 'bytes' }], + [module.initData, '0x'], + ), + ], + ) + case 'hook': + return module.initData + case 'fallback': + return encodePacked( + ['bytes4', 'address', 'bytes'], + [ + module.selector!, + module.hook!, + encodeAbiParameters( + [{ type: 'bytes' }, { type: 'bytes' }], + [ + encodePacked( + ['bytes1', 'bytes'], + [module.callType!, module.initData], + ), + '0x', + ], + ), + ], + ) + default: + throw new Error(`Unknown module type ${module.type}`) + } +} diff --git a/src/account/kernel/api/encodeModuleUninstallationData.ts b/src/account/kernel/api/encodeModuleUninstallationData.ts new file mode 100644 index 0000000..705a63e --- /dev/null +++ b/src/account/kernel/api/encodeModuleUninstallationData.ts @@ -0,0 +1,27 @@ +import { Account } from '../../types' +import { Hex, PublicClient, encodePacked } from 'viem' +import { KernelModule } from '../types' + +export const encodeModuleUninstallationData = async ({ + client, + account, + module, +}: { + client: PublicClient + account: Account + module: KernelModule +}): Promise => { + switch (module.type) { + case 'validator': + case 'executor': + case 'hook': + return module.deInitData + case 'fallback': + return encodePacked( + ['bytes4', 'bytes'], + [module.selector!, module.deInitData], + ) + default: + throw new Error(`Unknown module type ${module.type}`) + } +} diff --git a/src/account/kernel/api/uninstallModule.ts b/src/account/kernel/api/uninstallModule.ts index 743d411..b5cf8ca 100644 --- a/src/account/kernel/api/uninstallModule.ts +++ b/src/account/kernel/api/uninstallModule.ts @@ -52,7 +52,7 @@ const _uninstallModule = async ({ args: [ BigInt(kernelModuleTypeIds[module.type]), module.module, - module.initData || '0x', + module.deInitData, ], }) @@ -100,7 +100,7 @@ const _uninstallFallback = async ({ module.module, encodePacked( ['bytes4', 'bytes'], - [module.selector!, module.initData || '0x'], + [module.selector!, module.deInitData], ), ], }) diff --git a/src/account/kernel/index.ts b/src/account/kernel/index.ts index 39bca43..d63a79b 100644 --- a/src/account/kernel/index.ts +++ b/src/account/kernel/index.ts @@ -12,6 +12,8 @@ import { uninstallModule } from './api/uninstallModule' import { KernelModule, KernelModuleType } from './types' import { encode1271Signature } from './api/encode1271Signature' import { encode1271Hash } from './api/encode1271Hash' +import { encodeModuleInstallationData } from './api/encodeModuleInstallationData' +import { encodeModuleUninstallationData } from './api/encodeModuleUninstallationData' export class KernelImplementation { getInstalledModules = async ({ @@ -77,4 +79,26 @@ export class KernelImplementation { }: ERC1271HashParams): Hex => { return encode1271Hash({ account, chainId, validator, hash }) } + + encodeModuleInstallationData = ({ + account, + module, + }: { + account: Account + module: KernelModule + }): Hex => { + return encodeModuleInstallationData({ account, module }) + } + + encodeModuleUninstallationData = async ({ + client, + account, + module, + }: { + client: PublicClient + account: Account + module: KernelModule + }): Promise => { + return await encodeModuleUninstallationData({ client, account, module }) + } } diff --git a/src/account/kernel/types.ts b/src/account/kernel/types.ts index 3947e44..bb5c1f0 100644 --- a/src/account/kernel/types.ts +++ b/src/account/kernel/types.ts @@ -3,8 +3,9 @@ import { Address, Hex } from 'viem' export type KernelModule = { module: Address - initData?: Hex - additionalContext?: Hex + initData: Hex + deInitData: Hex + additionalContext: Hex type: KernelModuleType hook?: Address selector?: Hex diff --git a/src/account/nexus/api/encodeModuleInstallationData.ts b/src/account/nexus/api/encodeModuleInstallationData.ts new file mode 100644 index 0000000..48466ae --- /dev/null +++ b/src/account/nexus/api/encodeModuleInstallationData.ts @@ -0,0 +1,29 @@ +import { Account } from '../../types' +import { Hex, encodePacked } from 'viem' +import { Module, CallType } from '../../../module/types' + +export const encodeModuleInstallationData = ({ + account, + module, +}: { + account: Account + module: Module +}): Hex => { + switch (module.type) { + case 'validator': + case 'executor': + case 'hook': + return module.initData + case 'fallback': + return encodePacked( + ['bytes4', 'bytes1', 'bytes'], + [ + module.selector!, + module.callType ?? CallType.CALLTYPE_SINGLE, + module.initData, + ], + ) + default: + throw new Error(`Unknown module type ${module.type}`) + } +} diff --git a/src/account/nexus/api/encodeModuleUninstallationData.ts b/src/account/nexus/api/encodeModuleUninstallationData.ts new file mode 100644 index 0000000..8ca1e4f --- /dev/null +++ b/src/account/nexus/api/encodeModuleUninstallationData.ts @@ -0,0 +1,37 @@ +import { Account } from '../../types' +import { Hex, PublicClient, encodePacked, encodeAbiParameters } from 'viem' +import { Module, CallType } from '../../../module/types' +import { getPreviousModule } from '../../../common' + +export const encodeModuleUninstallationData = async ({ + client, + account, + module, +}: { + client: PublicClient + account: Account + module: Module +}): Promise => { + switch (module.type) { + case 'validator': + case 'executor': + const prev = await getPreviousModule({ client, account, module }) + return encodeAbiParameters( + [ + { name: 'prev', type: 'address' }, + { name: 'disableModuleData', type: 'bytes' }, + ], + [prev, module.deInitData], + ) + + case 'hook': + return module.deInitData + case 'fallback': + return encodePacked( + ['bytes4', 'bytes'], + [module.selector!, module.deInitData], + ) + default: + throw new Error(`Unknown module type ${module.type}`) + } +} diff --git a/src/account/nexus/api/uninstallModule.ts b/src/account/nexus/api/uninstallModule.ts index 9454a6e..5b81992 100644 --- a/src/account/nexus/api/uninstallModule.ts +++ b/src/account/nexus/api/uninstallModule.ts @@ -50,7 +50,7 @@ const _uninstallModule = async ({ const isInstalled = await isModuleInstalled({ client, account, module }) if (isInstalled) { - let moduleData = module.initData || '0x' + let moduleData = module.deInitData if (module.type === 'validator' || module.type === 'executor') { const prev = await getPreviousModule({ client, account, module }) moduleData = encodeAbiParameters( @@ -111,7 +111,7 @@ const _uninstallFallback = async ({ module.module, encodePacked( ['bytes4', 'bytes'], - [module.selector!, module.initData ?? '0x'], + [module.selector!, module.deInitData], ), ], }) diff --git a/src/account/nexus/index.ts b/src/account/nexus/index.ts index a785244..0c7f7bb 100644 --- a/src/account/nexus/index.ts +++ b/src/account/nexus/index.ts @@ -12,6 +12,8 @@ import { isModuleInstalled } from './api/isModuleInstalled' import { uninstallModule } from './api/uninstallModule' import { encode1271Signature } from './api/encode1271Signature' import { encode1271Hash } from './api/encode1271Hash' +import { encodeModuleInstallationData } from './api/encodeModuleInstallationData' +import { encodeModuleUninstallationData } from './api/encodeModuleUninstallationData' export class NexusImplementation { getInstalledModules = async ({ @@ -77,4 +79,26 @@ export class NexusImplementation { }: ERC1271HashParams): Hex => { return encode1271Hash({ account, chainId, validator, hash }) } + + encodeModuleInstallationData = ({ + account, + module, + }: { + account: Account + module: Module + }): Hex => { + return encodeModuleInstallationData({ account, module }) + } + + encodeModuleUninstallationData = async ({ + client, + account, + module, + }: { + client: PublicClient + account: Account + module: Module + }): Promise => { + return await encodeModuleUninstallationData({ client, account, module }) + } } diff --git a/src/account/safe/api/encodeModuleInstallationData.ts b/src/account/safe/api/encodeModuleInstallationData.ts new file mode 100644 index 0000000..8d67ae0 --- /dev/null +++ b/src/account/safe/api/encodeModuleInstallationData.ts @@ -0,0 +1,33 @@ +import { Account } from '../../types' +import { Hex, encodeAbiParameters, parseAbiParameters } from 'viem' +import { Module } from '../../../module/types' + +export const encodeModuleInstallationData = ({ + account, + module, +}: { + account: Account + module: Module +}): Hex => { + switch (module.type) { + case 'validator': + case 'executor': + return module.initData + case 'hook': + return encodeAbiParameters( + parseAbiParameters( + 'uint8 hookType, bytes4 selector, bytes memory initData', + ), + [module.hookType!, module.selector!, module.initData || '0x'], + ) + case 'fallback': + return encodeAbiParameters( + parseAbiParameters( + 'bytes4 functionSig, bytes1 calltype, bytes memory initData', + ), + [module.functionSig!, module.callType!, module.initData || '0x'], + ) + default: + throw new Error(`Unknown module type ${module.type}`) + } +} diff --git a/src/account/safe/api/encodeModuleUninstallationData.ts b/src/account/safe/api/encodeModuleUninstallationData.ts new file mode 100644 index 0000000..8d87392 --- /dev/null +++ b/src/account/safe/api/encodeModuleUninstallationData.ts @@ -0,0 +1,47 @@ +import { Account } from '../../types' +import { + Hex, + PublicClient, + parseAbiParameters, + encodeAbiParameters, +} from 'viem' +import { Module } from '../../../module/types' +import { getPreviousModule } from '../../../common' + +export const encodeModuleUninstallationData = async ({ + client, + account, + module, +}: { + client: PublicClient + account: Account + module: Module +}): Promise => { + switch (module.type) { + case 'validator': + case 'executor': + const prev = await getPreviousModule({ client, account, module }) + return encodeAbiParameters( + [ + { name: 'prev', type: 'address' }, + { name: 'disableModuleData', type: 'bytes' }, + ], + [prev, module.deInitData], + ) + case 'hook': + return encodeAbiParameters( + parseAbiParameters( + 'uint8 hookType, bytes4 selector, bytes memory deInitData', + ), + [module.hookType!, module.selector!, module.deInitData], + ) + + case 'fallback': + return encodeAbiParameters( + parseAbiParameters('bytes4 functionSig, bytes memory moduleDeInitData'), + [module.functionSig!, module.deInitData], + ) + default: + throw new Error(`Unknown module type ${module.type}`) + } +} diff --git a/src/account/safe/api/uninstallModule.ts b/src/account/safe/api/uninstallModule.ts index 2c0cc39..2728a9f 100644 --- a/src/account/safe/api/uninstallModule.ts +++ b/src/account/safe/api/uninstallModule.ts @@ -94,18 +94,18 @@ const getModuleCalldata = (module: Module): Hex => { switch (module.type) { case 'validator': case 'executor': - return module.initData || '0x' + return module.deInitData case 'hook': return encodeAbiParameters( parseAbiParameters( - 'uint8 hookType, bytes4 selector, bytes memory initData', + 'uint8 hookType, bytes4 selector, bytes memory deInitData', ), - [module.hookType!, module.selector!, module.initData || '0x'], + [module.hookType!, module.selector!, module.deInitData], ) case 'fallback': return encodeAbiParameters( parseAbiParameters('bytes4 functionSig, bytes memory moduleDeInitData'), - [module.functionSig!, module.initData || '0x'], + [module.functionSig!, module.deInitData], ) default: throw new Error(`Unknown module type ${module.type}`) diff --git a/src/account/safe/index.ts b/src/account/safe/index.ts index 472b4b4..ba20091 100644 --- a/src/account/safe/index.ts +++ b/src/account/safe/index.ts @@ -12,6 +12,8 @@ import { uninstallModule } from './api/uninstallModule' import { Module, ModuleType } from '../../module' import { encode1271Signature } from './api/encode1271Signature' import { encode1271Hash } from './api/encode1271Hash' +import { encodeModuleInstallationData } from './api/encodeModuleInstallationData' +import { encodeModuleUninstallationData } from './api/encodeModuleUninstallationData' export class SafeImplementation { getInstalledModules = async ({ @@ -77,4 +79,26 @@ export class SafeImplementation { }: ERC1271HashParams): Hex => { return encode1271Hash({ account, chainId, validator, hash }) } + + encodeModuleInstallationData = ({ + account, + module, + }: { + account: Account + module: Module + }): Hex => { + return encodeModuleInstallationData({ account, module }) + } + + encodeModuleUninstallationData = async ({ + client, + account, + module, + }: { + client: PublicClient + account: Account + module: Module + }): Promise => { + return await encodeModuleUninstallationData({ client, account, module }) + } }