@etherspot/modular-sdk
Version:
Etherspot Modular SDK - build with ERC-7579 smart accounts modules
147 lines • 7.84 kB
JavaScript
import { MODULE_TYPE } from '../common/index.js';
import { Networks } from '../network/index.js';
import { ErrorHandler } from '../errorHandler/errorHandler.service.js';
import { encodeAbiParameters, encodeFunctionData, parseAbi } from 'viem';
import { accountAbi } from '../common/abis.js';
import { getHookMultiPlexerInitData, HookMultiplexer } from './utils.js';
export class Pulse {
constructor(modularSdk) {
this.modularSdk = modularSdk;
}
/**
* Installs the complete Pulse ecosystem including:
* - Hook Multiplexer with Credible Account Module as subhook
* - Credible Account Module as Validator
* - Resource Lock Validator
*
* @param config Configuration options for the installation
* @returns Promise<string> UserOperation hash
*/
async installPulseModules(config) {
const { credibleAccountModuleAddress, resourceLockValidatorAddress, uninstallOldHookMultiplexer = false } = config;
// Get network config
const chainId = this.modularSdk['chainId'];
const networkConfig = Networks[chainId];
if (!networkConfig) {
throw new ErrorHandler('Network configuration not found for chain ID: ' + chainId);
}
// Use provided addresses or fall back to network defaults
const HOOK_MULTIPLEXER_ADDRESS_V2 = networkConfig.contracts.hookMultiPlexerV2;
const CREDIBLE_ACCOUNT_MODULE_ADDRESS = credibleAccountModuleAddress;
const RESOURCE_LOCK_VALIDATOR_ADDRESS = resourceLockValidatorAddress;
const HOOK_MULTIPLEXER_ADDRESS = networkConfig.contracts.hookMultiPlexer;
if (!HOOK_MULTIPLEXER_ADDRESS_V2 || !CREDIBLE_ACCOUNT_MODULE_ADDRESS || !RESOURCE_LOCK_VALIDATOR_ADDRESS) {
throw new ErrorHandler('Required contract addresses not found in network configuration');
}
// Get wallet address
const address = (await this.modularSdk.getCounterFactualAddress());
// Clear existing UserOps from batch
await this.modularSdk.clearUserOpsFromBatch();
// Uninstall old hook multiplexer if requested and installed
if (uninstallOldHookMultiplexer && HOOK_MULTIPLEXER_ADDRESS) {
await this.uninstallOldHookMultiplexer(address, HOOK_MULTIPLEXER_ADDRESS);
}
// Install Hook Multiplexer with Credible Account Module as subhook
await this.installHookMultiplexer(address, HOOK_MULTIPLEXER_ADDRESS_V2, CREDIBLE_ACCOUNT_MODULE_ADDRESS);
// Install Credible Account Module as Validator
await this.installCredibleAccountValidator(address, CREDIBLE_ACCOUNT_MODULE_ADDRESS);
// Install Resource Lock Validator
await this.installResourceLockValidator(address, RESOURCE_LOCK_VALIDATOR_ADDRESS);
// Estimate and send the UserOp
const op = await this.modularSdk.estimate();
const uoHash = await this.modularSdk.send(op);
return uoHash;
}
/**
* Checks if the Pulse modules are fully installed
*/
async isPulseModulesInstalled(config) {
const chainId = this.modularSdk['chainId'];
const networkConfig = Networks[chainId];
const HOOK_MULTIPLEXER_ADDRESS_V2 = networkConfig.contracts.hookMultiPlexerV2;
const CREDIBLE_ACCOUNT_MODULE_ADDRESS = config.credibleAccountModuleAddress;
const RESOURCE_LOCK_VALIDATOR_ADDRESS = config.resourceLockValidatorAddress;
const [hookMultiPlexer, credibleAccountValidator, resourceLockValidator] = await Promise.all([
this.modularSdk.isModuleInstalled(MODULE_TYPE.HOOK, HOOK_MULTIPLEXER_ADDRESS_V2),
this.modularSdk.isModuleInstalled(MODULE_TYPE.VALIDATOR, CREDIBLE_ACCOUNT_MODULE_ADDRESS),
this.modularSdk.isModuleInstalled(MODULE_TYPE.VALIDATOR, RESOURCE_LOCK_VALIDATOR_ADDRESS),
]);
return {
hookMultiPlexer,
credibleAccountValidator,
resourceLockValidator,
};
}
async uninstallOldHookMultiplexer(address, oldHookMultiplexerAddress) {
const isOldModuleInstalled = await this.modularSdk.isModuleInstalled(MODULE_TYPE.HOOK, oldHookMultiplexerAddress);
if (isOldModuleInstalled) {
const hmpDeinitData = encodeAbiParameters([{ type: 'uint256' }], [BigInt(MODULE_TYPE.HOOK)]);
const oldHmpUninstallCalldata = encodeFunctionData({
abi: parseAbi(accountAbi),
functionName: 'uninstallModule',
args: [BigInt(MODULE_TYPE.HOOK), oldHookMultiplexerAddress, hmpDeinitData],
});
await this.modularSdk.addUserOpsToBatch({ to: address, data: oldHmpUninstallCalldata });
}
}
async installHookMultiplexer(address, hookMultiplexerAddress, credibleAccountModuleAddress) {
const hmpInitData = getHookMultiPlexerInitData([credibleAccountModuleAddress]);
// const config = _makeBootstrapConfig(hookMultiplexerAddress, hmpInitData);
const hmpInstallCalldata = encodeFunctionData({
abi: parseAbi(accountAbi),
functionName: 'installModule',
args: [BigInt(MODULE_TYPE.HOOK), hookMultiplexerAddress, hmpInitData],
});
await this.modularSdk.addUserOpsToBatch({ to: address, data: hmpInstallCalldata });
}
async installCredibleAccountValidator(address, credibleAccountModuleAddress) {
const cavInitData = encodeAbiParameters([{ type: 'uint256' }], [BigInt(MODULE_TYPE.VALIDATOR)]);
const cavInstallCalldata = encodeFunctionData({
abi: parseAbi(accountAbi),
functionName: 'installModule',
args: [BigInt(MODULE_TYPE.VALIDATOR), credibleAccountModuleAddress, cavInitData],
});
await this.modularSdk.addUserOpsToBatch({ to: address, data: cavInstallCalldata });
}
async installResourceLockValidator(address, resourceLockValidatorAddress) {
const rlvInitData = encodeAbiParameters([{ type: 'address' }], [this.modularSdk.getEOAAddress()]);
const rlvInstallCalldata = encodeFunctionData({
abi: parseAbi(accountAbi),
functionName: 'installModule',
args: [BigInt(MODULE_TYPE.VALIDATOR), resourceLockValidatorAddress, rlvInitData],
});
await this.modularSdk.addUserOpsToBatch({ to: address, data: rlvInstallCalldata });
}
async addHook(hookAddress, hookType, hookMultiplexerAddress) {
// Get network config
const chainId = this.modularSdk['chainId'];
const networkConfig = Networks[chainId];
if (!networkConfig) {
throw new ErrorHandler('Network configuration not found for chain ID: ' + chainId);
}
// Use provided address or fall back to network default
const HOOK_MULTIPLEXER_ADDRESS = (hookMultiplexerAddress || networkConfig.contracts.hookMultiPlexer);
if (!HOOK_MULTIPLEXER_ADDRESS) {
throw new ErrorHandler('Hook Multiplexer address not found in network configuration');
}
const address = (await this.modularSdk.getCounterFactualAddress());
// Clear existing UserOps from batch
await this.modularSdk.clearUserOpsFromBatch();
// Encode the addHook function call
const addHookCalldata = encodeFunctionData({
abi: HookMultiplexer,
functionName: 'addHook',
args: [hookAddress, hookType],
});
// Add the call to the batch (calling addHook on the Hook Multiplexer)
await this.modularSdk.addUserOpsToBatch({
to: HOOK_MULTIPLEXER_ADDRESS,
data: addHookCalldata,
});
// Estimate and send the UserOp
const op = await this.modularSdk.estimate();
const uoHash = await this.modularSdk.send(op);
return uoHash;
}
}
//# sourceMappingURL=pulse.js.map