@etherspot/modular-sdk
Version:
Etherspot Modular SDK - build with ERC-7579 smart accounts modules
289 lines • 13.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EtherspotWalletAPI = void 0;
const viem_1 = require("viem");
const abis_js_1 = require("../common/abis.js");
const getInstalledModules_js_1 = require("../common/getInstalledModules.js");
const index_js_1 = require("../common/index.js");
const viem_utils_js_1 = require("../common/utils/viem-utils.js");
const constants_js_1 = require("../network/constants.js");
const bignumber_js_1 = require("../types/bignumber.js");
const BaseAccountAPI_js_1 = require("./BaseAccountAPI.js");
const Bootstrap_js_1 = require("./Bootstrap.js");
const SENTINEL_ADDRESS = (0, viem_1.getAddress)("0x0000000000000000000000000000000000000001");
const ADDRESS_ZERO = (0, viem_1.getAddress)("0x0000000000000000000000000000000000000000");
class EtherspotWalletAPI extends BaseAccountAPI_js_1.BaseAccountAPI {
constructor(params) {
super(params);
this.index = params.index ?? 0;
this.predefinedAccountAddress = params.predefinedAccountAddress ?? null;
if (params?.optionsLike) {
this.bootstrapAddress = constants_js_1.Networks[params.optionsLike.chainId]?.contracts?.bootstrap ?? constants_js_1.DEFAULT_BOOTSTRAP_ADDRESS;
}
else {
this.bootstrapAddress = constants_js_1.DEFAULT_BOOTSTRAP_ADDRESS;
}
}
getEOAAddress() {
return this.services.walletService.EOAAddress ?? '0x';
}
async isModuleInstalled(moduleTypeId, module, initData = '0x') {
const accountAddress = await this.getAccountAddress();
if (!accountAddress)
throw new Error('Account address not found');
const response = await this.publicClient.readContract({
address: accountAddress,
abi: (0, viem_1.parseAbi)(abis_js_1.accountAbi),
functionName: 'isModuleInstalled',
args: [moduleTypeId, module, initData]
});
return response;
}
async installModule(moduleTypeId, module, initData = '0x') {
const accountAddress = await this.getAccountAddress();
if (!accountAddress)
throw new Error('Account address not found');
if (await this.isModuleInstalled(moduleTypeId, module, initData)) {
throw new Error('the module is already installed');
}
return (0, viem_1.encodeFunctionData)({
functionName: 'installModule',
abi: (0, viem_1.parseAbi)(abis_js_1.accountAbi),
args: [moduleTypeId, module, initData],
});
}
async uninstallModule(moduleTypeId, module, deinitData) {
const isModuleInstalled = await this.isModuleInstalled(moduleTypeId, module, deinitData);
if (!isModuleInstalled) {
throw new Error('he module is not installed in the wallet');
}
if (moduleTypeId === index_js_1.MODULE_TYPE.EXECUTOR || moduleTypeId === index_js_1.MODULE_TYPE.VALIDATOR) {
const installedModules = moduleTypeId === index_js_1.MODULE_TYPE.EXECUTOR ? await this.getAllExecutors() : await this.getAllValidators();
if (installedModules.length === 1) {
throw new Error('Cannot uninstall the only module');
}
}
return (0, viem_1.encodeFunctionData)({
functionName: 'uninstallModule',
abi: (0, viem_1.parseAbi)(abis_js_1.accountAbi),
args: [moduleTypeId, module, deinitData],
});
}
async getAllExecutors(pageSize = constants_js_1.DEFAULT_QUERY_PAGE_SIZE) {
if (!this.accountAddress) {
throw new Error('Account address not found');
}
return await (0, getInstalledModules_js_1.getInstalledModules)({ client: this.publicClient, moduleAddress: (0, viem_utils_js_1.getViemAddress)(this.accountAddress), moduleTypes: ['executor'], pageSize: pageSize });
}
async getPreviousAddress(targetAddress, moduleTypeId) {
if (moduleTypeId !== index_js_1.MODULE_TYPE.EXECUTOR && moduleTypeId !== index_js_1.MODULE_TYPE.VALIDATOR) {
throw new Error("Unsupported module type");
}
const insalledModules = moduleTypeId === index_js_1.MODULE_TYPE.EXECUTOR ? await this.getAllExecutors() : await this.getAllValidators();
const index = insalledModules.indexOf(targetAddress);
if (index === 0) {
return SENTINEL_ADDRESS;
}
else if (index > 0) {
return insalledModules[index - 1];
}
else {
throw new Error(`Module ${targetAddress} not found in installed modules`);
}
}
async generateModuleDeInitData(moduleTypeId, module, deinitDataBase) {
if (moduleTypeId !== index_js_1.MODULE_TYPE.EXECUTOR && moduleTypeId !== index_js_1.MODULE_TYPE.VALIDATOR) {
throw new Error("Unsupported module type");
}
const previousAddress = await this.getPreviousAddress(module, moduleTypeId);
const deInitDataGenerated = (0, viem_1.encodeAbiParameters)((0, viem_1.parseAbiParameters)('address, bytes'), [previousAddress, deinitDataBase]);
return deInitDataGenerated;
}
async getAllValidators(pageSize = constants_js_1.DEFAULT_QUERY_PAGE_SIZE) {
if (!this.accountAddress) {
throw new Error('Account address not found');
}
return await (0, getInstalledModules_js_1.getInstalledModules)({ client: this.publicClient, moduleAddress: (0, viem_utils_js_1.getViemAddress)(this.accountAddress), moduleTypes: ['validator'], pageSize: pageSize });
}
async getActiveHook() {
const activeHook = await this.publicClient.readContract({
address: this.accountAddress,
abi: (0, viem_1.parseAbi)(abis_js_1.accountAbi),
functionName: 'getActiveHook',
});
return activeHook;
}
async getFallbacks() {
return [];
}
async getAllModules(pageSize = constants_js_1.DEFAULT_QUERY_PAGE_SIZE) {
const validators = await this.getAllValidators(pageSize) || [];
const executors = await this.getAllExecutors(pageSize) || [];
const hook = await this.getActiveHook() || "";
const fallbacks = await this.getFallbacks() || [];
return {
validators,
executors,
hook,
fallbacks
};
}
async checkAccountAddress(address) {
const eoaAddress = await this.getEOAAddress();
const isOwner = await this.publicClient.readContract({
address: address,
abi: (0, viem_1.parseAbi)(abis_js_1.accountAbi),
functionName: 'isOwner',
args: [eoaAddress]
});
if (!isOwner) {
throw new Error('the specified accountAddress does not belong to the given EOA provider');
}
else {
this.accountAddress = address;
}
}
async getInitCodeData() {
if (!this.validatorAddress) {
throw new Error('Validator address not found');
}
const validators = (0, Bootstrap_js_1.makeBootstrapConfig)(this.validatorAddress, '0x');
const executors = (0, Bootstrap_js_1.makeBootstrapConfig)(ADDRESS_ZERO, '0x');
const hook = (0, Bootstrap_js_1._makeBootstrapConfig)(ADDRESS_ZERO, '0x');
const fallbacks = (0, Bootstrap_js_1.makeBootstrapConfig)(ADDRESS_ZERO, '0x');
const initMSAData = (0, viem_1.encodeFunctionData)({
functionName: 'initMSA',
abi: (0, viem_1.parseAbi)(abis_js_1.bootstrapAbi),
args: [validators, executors, hook, fallbacks],
});
const eoaAddress = await this.getEOAAddress();
const initCode = (0, viem_1.encodeAbiParameters)((0, viem_1.parseAbiParameters)('address, address, bytes'), [eoaAddress, this.bootstrapAddress, initMSAData]);
return initCode;
}
async getAccountInitCode() {
if (this.factoryAddress == null || this.factoryAddress == '') {
throw new Error('no factory to get initCode');
}
const initCode = await this.getInitCodeData();
const salt = (0, viem_1.pad)((0, viem_1.toHex)(this.index), { size: 32 });
const functionData = (0, viem_1.encodeFunctionData)({
functionName: 'createAccount',
abi: (0, viem_1.parseAbi)(abis_js_1.factoryAbi),
args: [
salt,
initCode,
],
});
return (0, viem_1.concat)([
this.factoryAddress,
functionData,
]);
}
async getCounterFactualAddress() {
if (this.predefinedAccountAddress) {
await this.checkAccountAddress(this.predefinedAccountAddress);
}
const salt = (0, viem_1.pad)((0, viem_1.toHex)(this.index), { size: 32 });
const initCode = await this.getInitCodeData();
if (!this.accountAddress) {
this.accountAddress = (await this.publicClient.readContract({
address: this.factoryAddress,
abi: (0, viem_1.parseAbi)(abis_js_1.factoryAbi),
functionName: 'getAddress',
args: [salt, initCode]
}));
}
return this.accountAddress;
}
async getNonce(key = bignumber_js_1.BigNumber.from(0)) {
const accountAddress = await this.getAccountAddress();
const nonceKey = key.eq(0) ? this.validatorAddress : key.toHexString();
if (!nonceKey) {
throw new Error('nonce key not defined');
}
if (!this.checkAccountPhantom()) {
let isAddressIndicator = false;
try {
isAddressIndicator = (0, viem_1.isAddress)((0, viem_1.getAddress)(nonceKey), { strict: true });
if (!isAddressIndicator) {
throw new Error(`Invalid Validator Address: ${nonceKey}`);
}
else {
const isModuleInstalled = await this.isModuleInstalled(index_js_1.MODULE_TYPE.VALIDATOR, nonceKey);
if (!isModuleInstalled) {
throw new Error(`Validator: ${nonceKey} is not installed in the wallet`);
}
}
}
catch (e) {
console.error(`Error caught : ${e}`);
throw new Error(`Invalid Validator Address: ${nonceKey}`);
}
}
const dummyKey = (0, viem_1.getAddress)(nonceKey) + "00000000";
const nonceResponse = await this.publicClient.readContract({
address: this.entryPointAddress,
abi: (0, viem_1.parseAbi)(abis_js_1.entryPointAbi),
functionName: 'getNonce',
args: [accountAddress, BigInt(dummyKey)]
});
return nonceResponse;
}
async encodeExecute(target, value, data) {
const executeMode = (0, index_js_1.getExecuteMode)({
callType: index_js_1.CALL_TYPE.SINGLE,
execType: index_js_1.EXEC_TYPE.DEFAULT
});
let valueToProcess;
if (bignumber_js_1.BigNumber.isBigNumber(value)) {
valueToProcess = value.toString();
}
else if ((0, viem_1.isBytes)(value)) {
valueToProcess = new Uint8Array(value);
}
else {
if (typeof value === 'object' && value !== null && 'length' in value) {
valueToProcess = new Uint8Array(Object.values(value));
}
else {
valueToProcess = value;
}
}
const calldata = (0, viem_1.concat)([
target,
(0, viem_1.pad)((0, viem_1.toHex)(valueToProcess), { size: 32 }),
data
]);
return (0, viem_1.encodeFunctionData)({
functionName: 'execute',
abi: (0, viem_1.parseAbi)(abis_js_1.accountAbi),
args: [executeMode, calldata],
});
}
async signUserOpHash(userOpHash) {
return await this.services.walletService.signUserOp(userOpHash);
}
async encodeBatch(targets, values, datas) {
const executeMode = (0, index_js_1.getExecuteMode)({
callType: index_js_1.CALL_TYPE.BATCH,
execType: index_js_1.EXEC_TYPE.DEFAULT
});
const result = targets.map((target, index) => ({
target: target,
value: values[index],
callData: datas[index]
}));
const convertedResult = result.map(item => ({
...item,
value: typeof item.value === 'bigint' ? item.value : bignumber_js_1.BigNumber.from(item.value.toString()).toBigInt(),
}));
const calldata = (0, viem_1.encodeAbiParameters)((0, viem_1.parseAbiParameters)('(address target,uint256 value,bytes callData)[]'), [convertedResult]);
return (0, viem_1.encodeFunctionData)({
functionName: 'execute',
abi: (0, viem_1.parseAbi)(abis_js_1.accountAbi),
args: [executeMode, calldata],
});
}
}
exports.EtherspotWalletAPI = EtherspotWalletAPI;
//# sourceMappingURL=EtherspotWalletAPI.js.map